【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;
}

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

时间: 2024-10-12 04:30:37

【openjudge】C15C Rabbit's Festival CDQ分治+并查集的相关文章

[CDQ分治 并查集] BZOJ 3237 [Ahoi2013]连通图

考虑CDQ分治 把这半边对后半边没有影响的操作做了 然后分治 用并查集维护 开个栈暴力还原 #include<cstdio> #include<cstdlib> using namespace std; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; } re

【CF603E】Pastoral Oddities cdq分治+并查集

[CF603E]Pastoral Oddities 题意:有n个点,依次加入m条边权为$l_i$的无向边,每次加入后询问:当前图是否存在一个生成子图,满足所有点的度数都是奇数.如果有,输出这个生成子图中边权最大的边的权值最小可能是多少. $n\le 10^5,m\le 10^6,l_i\le 10^9$ 题解:可以证明如果存在一个生成子图满足所有点度数都是奇数,当且仅当所有连通块都有偶数个点.并且可以知道加边一定不会使答案更劣.正解有三种:1.LCT维护最小生成树:2.cdq分治(类似整体二分)

Open_POJ C15C Rabbit&#39;s Festival

http://poj.openjudge.cn/practice/C15C?lang=en_US n 点 m 边 k 天. 每条边在某一天会消失(仅仅那一天消失). 问每一天有多少对点可以相互到达. 这个一看就是并查集.但是呢.... “并查集是不能删边的”,这是这个问题的障碍. 并查集真的不能删边么?其实不是绝对的,如果删除的边是最后加入的边,并且并查集不进行路径压缩的话,自然是可以删除的. P.S. 并查集找根如果路径压缩加按秩合并的话是 $O(log(log(n)))$,如果只按秩合并的话

2015多校第6场 HDU 5354 Bipartite Graph CDQ,并查集

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5354 题意:求删去每个点后图是否存在奇环(n,m<=1e5) 解法:很经典的套路,和这题一样:http://www.cnblogs.com/spfa/p/7358672.html CDQ套并查集. 这题最开始是看了南神的代码才懂的,http://blog.csdn.net/hdu2014/article/details/47450709    因为要判断每一个点,而且一旦一个点之外的几个点形成了奇环

BZOJ 4025 二分图 分治+并查集

题目大意:给定一张n个点的图,有m条边,T个时间段,每条边只存在于(st,ed]这些时间段,求每个时间段内这个图是否是二分图 分治并查集大法好 定义Solve(x,y,E)为当前处理的区间为[x,y],E为所有存在时间为[x,y]的子集的边的集合 那么对于E中的每一条边(u,v),讨论: 若当前边的存在时间为[x,y],则在并查集上判断是否出现奇环 如果出现,[x,y]内的所有时刻都一定不是二分图,输出答案即可 如果不出现,在并查集中连接(u,v) 否则判断存在时间和mid的关系讨论扔进左区间还

[BZOJ4025]二分图(线段树分治,并查集)

4025: 二分图 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2191  Solved: 800[Submit][Status][Discuss] Description 神犇有一个n个节点的图.因为神犇是神犇,所以在T时间内一些边会出现后消失.神犇要求出每一时间段内这个图是否是二分图.这么简单的问题神犇当然会做了,于是他想考考你. Input 输入数据的第一行是三个整数n,m,T. 第2行到第m+1行,每行4个整数u,v,start,end

[hdu 5354] Bipartite Graph 分治 并查集

题意 给定一张 $n$ 个点, $m$ 条边的无向图. 问删去每个点后, 原图是不是二分图. $1 \le n, m \le {10} ^ 5$ . 分析 一个图是二分图 $\Leftrightarrow$ 图中不存在奇环. 判定一个图是不是二分图, 可以使用并查集, 多维护一个当前点与父亲的关系的量 bond . 删除每一个点, 我们有两种维度: 区间加法, 区间减法. 这里考虑区间加法, 即考虑分治. 由于要支持撤销, 所以使用按秩合并的并查集. 注意按照大小合并... 按深度合并会 TLE

[BZOJ3237][AHOI2013]连通图(分治并查集)

3237: [Ahoi2013]连通图 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1736  Solved: 655[Submit][Status][Discuss] Description Input Output Sample Input 4 5 1 2 2 3 3 4 4 1 2 4 3 1 5 2 2 3 2 1 2 Sample Output Connected Disconnected Connected HINT N<=1000

BZOJ4025 二分图 分治 并查集 二分图 并查集按秩合并 带权并查集

原文链接http://www.cnblogs.com/zhouzhendong/p/8683831.html 题目传送门 - BZOJ4025 题意 有$n$个点,有$m$条边.有$T$个时间段.其中第$i$条边连接节点$x_i,y_i$,并且在$start_i$时刻出现,在$end_i$时刻消失.问每一个时刻的图是不是二分图. $n\leq 10^5,m\leq 2\times 10^5,T\leq 10^5$ 题解 真是一道好题. 做这题我才发现我从来没写过按秩合并的并查集QAQ. 先考虑按