Test Message

c782: PC. 孤單值量測

內容

在一條馬路上有 n 個人,每個人都站在一個位置 a_i,都有一個孤單評測值 w_i。現在里長想計算這些人的孤單值總和,如果第 i 個人和第 j 個人的位置相差大於 k 公尺,那麼他們會對總孤單值 S 貢獻 w_i*w_j。現在,請你幫我求出 S。


輸入

輸入第一行有一個正整數 t(t≤5),表示一共有 t 筆測資。
每筆測資的第一行有兩個正整數 n(n≤2000000),k(k≤1000000000),第二行有 n 個整數 ai(0≤ai≤1000000000,1≤i≤n),輸入保證從小到大。
第三行有 n 個整數 wi(−1000≤wi≤1000,1≤i≤n)。

1
5 3
1 2 3 4 5
2 3 4 5 6

輸出

對每一筆測資,請輸出一個正整數 S。

12


解題思路

依據題意"輸入保證從小到大"所以當 i < j 時 a[i] 必小於 a[j],因此可以假設當第 i 項可以與第 j 項產生孤單值時,第 1~i 項都可以和第 j 項產生孤獨值,因為比 i 小的值與 j 的距離必定比 i 更遠。

所以先找到距離 j 點最近但距離又大於 k 的 i 點,並將所有 i 點紀錄至陣列 v[],如此一來之後要算孤獨單值時就不用再從 0 開始遍歷,直接查表 v[] 就好。

接下來計算孤獨值,因為孤獨值是互相的,所以只算往前的孤獨值避免重複計算。
假如 j = 5、 i = 3,且 j 和 i 可以產生孤獨值,那點 j 的的孤獨值就是

w[1] * w[5] + w[2] * w[5] + w[3] * w[5]

而根據乘法的分配律,可以將上式轉為

(w[1] + w[2] + w[3]) * w[5]

這樣在算孤獨值時只要先將答案加上 w[v[i]] * w[i],再把 w[i]加上 w[i-1],就可以省下迴圈做連續乘法的時間了。

本題輸入測資非常大,輸入優化可以壓掉不少時間。


完整程式碼

正常版

AC (0.3s, 23MB)
#include <stdio.h>
#define MAX 2000010

long long sum, w[MAX];
int kase, n, k;
int a[MAX], v[MAX];

int main()
{
scanf(" %d", &kase);
while (kase--)
{
sum = 0;
scanf(" %d %d", &n, &k);
for (int i = 1; i <= n; i++)
scanf(" %d", &a[i]);
for (int i = 1, s = 1; i <= n; i++)
{
while (a[i] - a[s] > k)
s++;
v[i] = s - 1;
}
for (int i = 1; i <= n; i++)
{
scanf(" %d", &w[i]);
sum += w[v[i]] * w[i];
w[i] += w[i - 1];
}
printf("%lld\n", sum);
}
return 0;
}

輸入優化版

AC (41ms, 24MB)
#pragma GCC optimize(3)
#include <stdio.h>
#define MAX 2000010
#define BUFMAX 1048576

long long sum, w[MAX];
int kase, n, k, length = BUFMAX, a[MAX], v[MAX];
char buf[BUFMAX], tmp, * pt = buf, * end = buf;

inline char freadChar()
{
if (pt == end)
{
if (length < BUFMAX)
return EOF;
length = fread(buf, 1, BUFMAX, stdin);
end = buf + length;
pt = buf;
}
return *pt++;
}

inline void freadUInt(int* val)
{
while ((tmp = freadChar()) < '0')
if (tmp == EOF) return;
*val = tmp - '0';
while ((tmp = freadChar()) >= '0')
* val = (*val << 3) + (*val << 1) + (tmp - '0');
}

inline void freadLongLong(long long* val)
{
while ((tmp = freadChar()) < '-')
if (tmp == EOF) return;
if (tmp == '-')
{
*val = freadChar() - '0';
while ((tmp = freadChar()) >= '0')
* val = (*val << 3) + (*val << 1) + (tmp - '0');
*val = -*val;
}
else
{
*val = tmp - '0';
while ((tmp = freadChar()) >= '0')
* val = (*val << 3) + (*val << 1) + (tmp - '0');
}
}

int main()
{
scanf(" %d", &kase);
while (kase--)
{
sum = 0;
freadUInt(&n), freadUInt(&k);
for (int i = 1; i <= n; i++)
freadUInt(&a[i]);
for (int i = 1, s = 1; i <= n; i++)
{
while (a[i] - a[s] > k)
s++;
v[i] = s - 1;
}
for (int i = 1; i <= n; i++)
{
freadLongLong(&w[i]);
sum += w[v[i]] * w[i];
w[i] += w[i - 1];
}
printf("%lld\n", sum);
}
return 0;
}