hdu 6058 并查集

题意: 给你一个全排列,要你求这个序列的所有区间的第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
",ans);
    }
    return 0;
}



原文地址:https://www.cnblogs.com/zhangxianlong/p/10672509.html