【openjudge】C15C Rabbit's Festival CDQ分治+并查集

题目链接:http://poj.openjudge.cn/practice/C15C/

题意:n 点 m 边 k 天。每条边在某一天会消失(仅仅那一天消失)。问每一天有多少对点可以相互到达。

解法:开始不会做,参考的YYN的题解:http://blog.csdn.net/u013368721/article/details/45725181

学习了这种CDQ加并查集的做法,可以说是非常的巧妙了。复杂度可以保证在:O(KlogklogK)的范围。

 

//CDQ + DSU
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100010;
const int maxe = 200010;
struct edge{
    int u,v,next;
    edge(){}
    edge(int u,int v,int next):u(u),v(v),next(next){}
}E[maxn*2];
int n, m, k, head[maxn], edgecnt;
struct Node{
    int u,v,cntu,cntv,rnku,rnkv;
    Node(){}
    Node(int u,int v,int cntu,int cntv,int rnku,int rnkv):u(u),v(v),cntu(cntu),cntv(cntv),rnku(rnku),rnkv(rnkv){}
};
Node S[maxe];
int cnt[maxn];
int p[maxn];
int rnk[maxn];
int top;
LL ans;
void init(){
    top = 0;
    ans = 0;
    edgecnt=0;
    memset(head,-1,sizeof(head));
}
void add(int id , int u, int v){
    E[edgecnt] = edge (u, v, head[id]);
    head[id] = edgecnt++;
}
int find_set(int x){
    int o = x;
    while(p[o] != o) o = p[o];
    return o;
}
void union_set(int l, int r){
    for(int t=l; t<=r; t++){
        for(int i=head[t]; ~i; i=E[i].next){
            int u = find_set(E[i].u);
            int v = find_set(E[i].v);
            if(u == v) continue;
            S[top++] = Node(u, v, cnt[u], cnt[v], rnk[u], rnk[v]);
            ans += (LL)cnt[u] * cnt[v];
            if(rnk[u] <= rnk[v]){
                rnk[v] = max(rnk[v], rnk[u]+1);
                p[u] = v;
                cnt[v] += cnt[u];
            }
            else{
                p[v] = u;
                cnt[u] += cnt[v];
            }
        }
    }
}
void Back(int x)
{
    while(top > x){
        --top;
        int u = S[top].u, v = S[top].v;
        ans -= (LL)S[top].cntu*S[top].cntv;
        p[u] = u;
        p[v] = v;
        cnt[u] = S[top].cntu;
        cnt[v] = S[top].cntv;
        rnk[u] = S[top].rnku;
        rnk[v] = S[top].rnkv;
    }
}
void CDQ(int l, int r)
{
    if(l == r){
        printf("%lld\n", ans);
        return;
    }
    int mid=(l+r)>>1;
    int rtop=top;
    union_set(mid+1,r);
    CDQ(l, mid);
    Back(rtop);
    union_set(l,mid);
    CDQ(mid+1,r);
    Back(rtop);
}
int main()
{
    while(~scanf("%d %d %d", &n,&m,&k))
    {
        init();
        for(int i=1; i<=n; i++){
            p[i]=i;
            cnt[i]=1;
            rnk[i]=0;
        }
        int u, v, c;
        for(int i=1; i<=m; i++){
            scanf("%d %d %d", &u,&v,&c);
            if(c > k){
                u = find_set(u), v = find_set(v);
                if(u == v) continue;
                if(rnk[u]<=rnk[v]){
                    rnk[v]=max(rnk[v],rnk[u]+1);
                    p[u]=v;
                    cnt[v]+=cnt[u];
                }
                else{
                    p[v]=u;
                    cnt[u]+=cnt[v];
                }
            }
            else{
                add(c, u, v);
            }
        }
        CDQ(1, k);
    }
    return 0;
}

 

posted @ 2017-08-14 16:47  zxycoder  阅读(261)  评论(0编辑  收藏  举报