HDU6058Kanade's sum(思维+链表模拟)

来源:互联网 发布:windows vnc客户端 编辑:程序博客网 时间:2024/06/16 22:21

题目链接

题意:

给出一个n和一个k,求1~n的每个区间的第k大的总和是多少,区间长度小于k的话,贡献为0.

分析:

我们只要求出对于一个数xx左边最近的kk个比他大的和右边最近kk个比他大的,扫一下就可以知道有几个区间的kk大值是xx.

我们考虑从小到大枚举xx,每次维护一个链表,链表里只有>=x>=x的数,那么往左往右找只要暴力跳kk次,删除也是O(1)O(1)的。

时间复杂度:O(nk)O(nk)

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int N = 500010;int Case, n, K, num[N], pos[N], pre[N], nxt[N];inline void del(int x){    pre[nxt[x]] = pre[x];    nxt[pre[x]] = nxt[x];}inline ll solve(int x){    int a[100], b[100];    int ca = 0, cb = 0;    for(int i = x; i; i = pre[i])    {        a[++ca] = i - pre[i];        if(ca == K) break;    }    for(int i = x; i <= n; i = nxt[i])    {        b[++cb] = nxt[i] - i;        if(cb == K) break;    }    ll t = 0;    for(int i = 1; i <= ca; i++)        if(K - i + 1 <= cb)            t += 1LL * a[i] * b[K-i+1];    return t;}int main(){    scanf("%d", &Case);    while(Case--)    {        scanf("%d%d", &n, &K);        for(int i = 1; i <= n; i++) scanf("%d", &num[i]), pos[num[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++)        {            int x = pos[i];            ans += solve(x) * i;            del(x);        }        printf("%I64d\n", ans);    }    return 0;}


原创粉丝点击