hdu 6058 并查集

来源:互联网 发布:股票抢单软件 编辑:程序博客网 时间:2024/05/29 02:16

题意: 给你一个全排列,要你求这个序列的所有区间的第k大的和


思路:一道链表好题。。虽然没用链表做。。这两天有机会用链表补一下。。

  拿到题首先分析数据性质,一个是全排列,一个是k比较小,因此我们可以考虑维护每个点周围比这个点向左大0--k-1的数有多少和向右。(相当于枚举这个点作为第K大的区间有多少个),这里注意的是要跳过比枚举点要小的数,如果可以o1跳过,那么单个点维护就是o(k)的复杂度。这里使用了并查集,慢于o1不过也莽过去了。。。


代码:

#include<bits/stdc++.h>using namespace std;#define MEM(a,b) memset(a,b,sizeof(a))#define PB push_back#define MP make_pair#define X first#define Y second#define bug puts("bug");typedef long long ll;const int maxn=1e6+10;int t,n,k,x;ll ans;int a[maxn],c[maxn],O[maxn],fa[maxn],L[maxn],R[maxn];int fin(int x){    if(fa[x]==x) return x;    return fa[x]=fin(fa[x]);}void update(int x,int y){    int fx=fin(x),fy=fin(y);    fa[fx]=fy;    R[fy]=max(R[fy],R[fx]),L[fy]=min(L[fy],L[fx]);}void sl(int x){    vector<ll> lk(k+1,x+1),rk(k+1,x-1);    int lid=x,rid=x;    int mk1=0,mk2=0;    for(int i=0;i<k&&lid>0;i++){        mk1=max(mk1,i);        lid=L[fin(lid)];        if(lid>0&&O[lid]==0&&O[lid-1]) lid=L[fin(lid-1)];        lk[i]=lid;        lid--;    }    for(int i=0;i<k&&rid<=n;i++){        mk2=max(mk2,i);        rid=R[fin(rid)];        if(rid<=n&&O[rid]==0&&O[rid+1]) rid=R[fin(rid+1)];        rk[i]=rid;        rid++;    }    int ll=x+1,rr=x-1;    for(int i=0;i<k;i++){        if(i>mk1||(k-i-1)>mk2) continue;        ll=x+1,rr=x-1;        if(i!=0) ll=lk[i-1];        if(k-i-1!=0)rr=rk[k-i-2];        ans+=(abs(lk[i]-ll))*(abs(rk[k-i-1]-rr))*c[x];    }}void modi(int x){    O[x]=1;    if(x-1>0&&O[x-1])update(x,x-1);    if(x+1<=n&&O[x+1])update(x,x+1);}int main(){    scanf("%d",&t);    while(t--){        ans=0;        MEM(O,0);        scanf("%d%d",&n,&k);        for(int i=1;i<=n;i++) fa[i]=L[i]=R[i]=i;        for(int i=1;i<=n;i++) scanf("%d",&c[i]),a[c[i]]=i;        for(int i=1;i<=n;i++){            modi(a[i]);            if(i<=n-k+1) sl(a[i]);        }        printf("%lld\n",ans);    }    return 0;}



原创粉丝点击