zoukankan      html  css  js  c++  java
  • 2020牛客暑期多校训练营(第八场)A All-Star Game

    题解:其实这题的本质就是维护连通块个数。

    线段树维护可撤销并查集,我们以每次修改作为时间,记录条边出现的时间段,并且用线段树维护这些时间段,线段树的每个结点开一个 (vector) 数组, 每个结点代表一个时间区间,若边 ((u, v)) 在节点 (rt) 对应的时间段存在,则 (vec[rt].push\_back(id[{u, v}])) 。具体代码如下

    void Update(int le, int ri, int L, int R, int id, int rt){
    	if(L <= le && ri <= R){
    		vec[rt].push_back(id);
    		return ;
    	}
    	int mid = (le + ri) >> 1;
    	if(L <= mid) Update(le, mid, L, R, id, lc);
    	if(R > mid) Update(mid + 1, ri, L, R, id, rc);
    }
    

    然后讲一讲可撤销并查集,首先可撤销并查集不可以路径压缩,所以我们要按秩合并,当我们添加一条边 ((u, v)) ,设 (fau)(u) 的祖先,(fav)(v) 的祖先,我们记录 (fau, fav, rank[fau], rank[fav]) ,用一个栈保存, 之后每次撤销一条边时,我们只要将 (fau, fav) 的属性还原就好了,代码如下

    void stkpop(){
    	fa[stk[top].fau] = stk[top].fau;
    	fa[stk[top].fav] = stk[top].fav;
    	Rank[stk[top].fau] = stk[top].rku;
    	Rank[stk[top].fav] = stk[top].rkv;
    	sum = stk[top].sum, ans = stk[top].ans; // 这两个后面会讲
    	top--;	
    }
    

    然后具体来讲讲怎么实现,我们从线段树的根节点开始遍历,每次优先遍历左儿子,当访问到一个节点时,我们遍历该节点的 (vector) ,讲所有边加入图中,并用并查集维护,若粉丝 (v) 之前没有偶像且偶像 (u) 之前也没有粉丝,让 (ans = ans + 1)(ans) 代表最后我们所需要的偶像的个数,若粉丝 (v) 之前有偶像 (vv) 且偶像 (u) 之前也有其他粉丝,则 (ans = ans - 1) , 代码如下

    int unit(int x, int y){
    	top++; // 记录未加入该边时图的状态
    	x = Find(x), y = Find(y);
    	stk[top].fau = x, stk[top].fav = y;
    	stk[top].rku = Rank[x], stk[top].rkv = Rank[y];
    	stk[top].sum = sum, stk[top].ans = ans;
    	if(x == y) return 0; // 若已子啊一个连通块中则是无效合并
    	if(x <= n && y > n) {
    		sum++;
    		if(Rank[x] == 1) ans++; // 偶像 x 没有粉丝
    	}
    	else {
    		if(Rank[x] != 1 && Rank[y] != 1){ //偶像 x,y 都已经有粉丝,这里的x, y已经是 find 过了的,所以代表的都是偶像
    			ans--; 
    		}
    	}
    	if(Rank[x] < Rank[y]) swap(x, y);
    	if(Rank[x] == Rank[y]) Rank[x]++;
    	fa[y] = x;
    	return 1;
    }
    

    以下是AC代码

    #include <bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define lc (rt << 1)
    #define rc ((rt << 1) | 1)
    typedef long long LL;
    typedef pair<int, int> pii;
    const int maxn = 5e5 + 50;
    int n, m, q;
    
    vector<int> vec[maxn << 2];
    void Update(int le, int ri, int L, int R, int id, int rt){
    	if(L <= le && ri <= R){
    		vec[rt].push_back(id);
    		return ;
    	}
    	int mid = (le + ri) >> 1;
    	if(L <= mid) Update(le, mid, L, R, id, lc);
    	if(R > mid) Update(mid + 1, ri, L, R, id, rc);
    }
    map<pii, int> mp;
    struct Edge
    {
    	int u, v;
    } edge[maxn * 10];
    int tot = 0;
    int le[maxn * 10], ri[maxn * 10];
    
    int ans = 0, sum = 0;
    struct qnode
    {
    	int fau, fav, rku, rkv, sum, ans;
    } stk[maxn * 10];
    int top;
    int fa[maxn], Rank[maxn];
    int Find(int x){
    	if(x == fa[x]) return x;
    	return Find(fa[x]);
    }
    int unit(int x, int y){
    	top++;
    	x = Find(x), y = Find(y);
    	stk[top].fau = x, stk[top].fav = y;
    	stk[top].rku = Rank[x], stk[top].rkv = Rank[y];
    	stk[top].sum = sum, stk[top].ans = ans;
    	if(x == y) return 0;
    	if(x <= n && y > n) {
    		sum++;
    		if(Rank[x] == 1) ans++;
    	}
    	else {
    		if(Rank[x] != 1 && Rank[y] != 1){
    			ans--; 
    		}
    	}
    	if(Rank[x] < Rank[y]) swap(x, y);
    	if(Rank[x] == Rank[y]) Rank[x]++;
    	fa[y] = x;
    	return 1;
    }
    
    void stkpop(){ 
    	fa[stk[top].fau] = stk[top].fau;
    	fa[stk[top].fav] = stk[top].fav;
    	Rank[stk[top].fau] = stk[top].rku;
    	Rank[stk[top].fav] = stk[top].rkv;
    	sum = stk[top].sum, ans = stk[top].ans;
    	top--;	
    }
    void Query(int le, int ri, int rt){
    	int len = vec[rt].size();
    	for(int i = 0; i < len; i++){
    		int id = vec[rt][i];
    		int u = edge[id].u, v = edge[id].v;
    		unit(u, v);
    	}
    	if(le == ri){
    		if(sum != m) printf("-1
    ");
    		else printf("%d
    ", ans);
    	} else {
    		int mid = (le + ri) >> 1;
    		Query(le, mid, lc);
    		Query(mid + 1, ri ,rc);
    	}
    	while(len--){
    		stkpop();
    	}
    }
    int main(int argc, char const *argv[])
    {
    	scanf("%d%d%d", &n, &m, &q);
    	for(int i = 1; i <= n + m; i++) {
    		Rank[i] = 1;
    		fa[i] = i;
    	}
    	for(int i = 1; i <= n; i++){
    		int k;
    		scanf("%d", &k);
    		while(k--){
    			int u = i;
    			int v;
    			scanf("%d", &v);
    			v += n;
    			if(!mp.count({u, v})) {
    				mp[{u, v}] = ++tot;
    				edge[tot] = {u, v};
    			}
    			le[tot] = 1; // 记录起点
    		}
    	}
    	for(int i = 1; i <= q; i++){
    		int u, v;
    		scanf("%d%d", &v, &u);
    		v += n;
    		if(!mp.count({u, v})) {
    			mp[{u, v}] = ++tot;
    			edge[tot] = {u, v};
    		}
    		int id = mp[{u, v}];
    		if(le[id] == 0) le[id] = i; // 如果这条边未记录过,记录左端点
    		else { // 否则在线段树上记录该区间
    			ri[id] = i - 1; 
    			if(i != 1){
    				Update(1, q, le[id], ri[id], id, 1);
    			}
    			le[id] = ri[id] = 0;
    		}
    	}
    	for(int i = 1; i <= tot; i++){
    		if(le[i]){
    			ri[i] = q;
    			Update(1, q, le[i], ri[i], i, 1);
    		}
    	}
    	Query(1, q, 1);
        return 0;
    }
    
  • 相关阅读:
    求助
    debian虚拟机试用
    算是业界新闻吧
    推荐一个图书分享网站
    日志
    写汇编报告小记
    水仙花数
    进程创建
    win32程序运行原理1
    [解题报告]374 Big Mod
  • 原文地址:https://www.cnblogs.com/PCCCCC/p/13441702.html
Copyright © 2011-2022 走看看