HDU 6058 Kanade's sum (区间第k大的数的贡献)

来源:互联网 发布:虚拟号码发短信软件 编辑:程序博客网 时间:2024/06/13 12:41

思路:

维护一个链表,初始状态为输入的序列。
然后从1开始到n,对每个数统计他在哪些区间中,然后乘以这个数,作为这个数做出的贡献。
统计他在某些区间的办法:

对于每个数i,我们向左找k-1个比他大的数(由于我们是从小到大处理的,且每个数处理后就在链表里删除了,所以这里我们直接一个个跳就好)。假设这第k-1个数为b,再向左找第k个为a,那么我们可以得知,(a,b】中的数皆可作为满足条件的区间的左端点。然后相似的考虑右端点,在这种情况下i的右边第一个比i大的数左边的数都可以作为满足条件的区间的右端点。
然后我们将区间整体向右走一跳。变成左边有k-2个比i大的,右边有1的比i大的,然后同理统计。
区间递推知道左端点为i。
至此我们将i对所有它出现的区间都统计了贡献。
然后我们就可以在链表里删除这个数。
然后接着处理下一个。

#include <iostream>#include <cmath>#include <cstdio>#include <cstring>#include <algorithm>typedef long long int lli;using namespace std;struct pp{    int tol,tor;}head[600000];int lis[600000];lli a[600000];// 自适应辛普森int main(){//    freopen("1003.in","r",stdin);//    freopen("my.txt","w",stdout);    int cas,n,k,v;    scanf("%d",&cas);    while(cas--){        scanf("%d%d",&n,&k);        for(int i = 1;i <= n;i++){            scanf("%d",&v);            lis[i] = v;            a[v] = i;            head[i].tol = i-1;            head[i].tor = i+1;        }        head[0].tor = 1;head[n+1].tor = 0;        lis[0] = 0;a[0] = 0;        lis[n+1] = n+1;a[n+1] = n+1;        lli ans = 0;        for(int i = 1;i <= n;i++){            int n1 = a[i],n2,n3 = a[i],j;            for(j = 1;j <= k-1 && n1;j++){                n1 = head[n1].tol;            }            if(!n1){                j--;                n1 = head[n1].tor;                for(;j <= k-1 && n3!=n+1;j++){                    n3 = head[n3].tor;                }                if(j != k) break;            }            for(int cnt = 1;cnt <= k && n1 <= a[i] &&n3;cnt++){                if(n3 != n+1)                    ans += (lli)i*(lli)(a[lis[n1]]-a[lis[head[n1].tol]])*(lli)(a[lis[head[n3].tor]]-a[lis[n3]]);                n1 = head[n1].tor;   n3 = head[n3].tor;            }            n1 = head[a[i]].tol,n2 = head[a[i]].tor;            head[n1].tor = n2;            head[n2].tol = n1;        }        printf("%lld\n",ans);    }    return 0;}
原创粉丝点击