題意
給出一個n和一個k,求1~n的每個區間的第k大的總和是多少,區間長度小於k的話,貢獻為0.
思路
首先有一個關系:當一個數是第k大的時候,前面有x個比它大的數,那么后面就有k-x-1個比它大的數。
比賽的時候隊友想出了用set來維護。一開始是一個空的set,先插入大的數,那么當之后插入數的時候,他們之間的pos距離就代表它有多少個小於它的,然后根據上面的關系,對於每個數最多使得迭代器跳k次,就可以快速維護了。其實想法和正解差不多,但是因為其迭代器使用不熟練,而且我還死磕自己錯誤的想法。
題解的思路其實差不多,一開始先維護一個滿的鏈表,然后從小到大刪除,每次算完一個數,就在鏈表里面刪除,算x的時候,保證刪除的數都比x小,都可以用來算貢獻。i和pre[i]和nxt[i]的距離就是小於當前的數的數目+1。
鏈表
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
typedef long long LL;
int pre[N], nxt[N], v[N], pos[N], n, k;
LL a[N], b[N];
LL solve(int x) {
int c1 = 0, c2 = 0;
for(int i = x; i && c1 <= k; i = pre[i])
a[++c1] = i - pre[i];
for(int i = x; i <= n && c2 <= k; i = nxt[i])
b[++c2] = nxt[i] - i;
LL ans = 0;
for(int i = 1; i <= c1; i++)
if(k - i + 1 <= c2 && k - i + 1 >= 1)
ans += a[i] * b[k-i+1];
return ans;
}
void del(int x) {
pre[nxt[x]] = pre[x];
nxt[pre[x]] = nxt[x];
}
int main() {
int t; scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) scanf("%d", &v[i]), pos[v[i]] = i;
for(int i = 0; i <= n + 1; i++) pre[i] = i - 1, nxt[i] = i + 1;
pre[0] = 0; nxt[n+1] = n + 1;
LL ans = 0;
for(int i = 1; i <= n; i++) {
ans += solve(pos[i]) * i;
del(pos[i]);
} printf("%lld\n", ans);
} return 0;
}
set
超時了。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
typedef long long LL;
set<int> se;
int pos[N];
int x[N], y[N];
int main() {
int t; scanf("%d", &t);
while(t--) {
int n, k, z; scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) scanf("%d", &z), pos[z] = i;
LL ans = 0;
se.clear();
se.insert(0); se.insert(n+1);
for(int i = n; i; i--) {
se.insert(pos[i]);
int c1 = 0, c2 = 0;
set<int>::iterator it = lower_bound(se.begin(), se.end(), pos[i]), it1, it2;
it1 = it, it2 = it;
while(c1 <= k && *it2 != 0) { // 向前面找k個
it2--;
x[++c1] = *it1 - *it2;
it1--;
}
it1 = it, it2 = it;
while(c2 <= k && *it2 != n + 1) { // 向后面找k個
it2++;
y[++c2] = *it2 - *it1;
it1++;
}
for(int j = 1; j <= c1; j++)
if(k - j + 1 <= c2) ans += 1LL * i * x[j] * y[k-j+1];
// printf("%d : %I64d\n", i, ans);
} printf("%I64d\n", ans);
} return 0;
}