zoukankan      html  css  js  c++  java
  • HihoCoder 1629 Graph (2017 ACM-ICPC 北京区域赛 C题,回滚莫队 + 启发式合并 + 可撤销并查集)

    题目链接  2017 ACM-ICPC Beijing Regional Contest Problem C

    题意  给定一个$n$个点$m$条边的无向图。现在有$q$个询问,每次询问格式为$[l, r]$,即图中只有第$l$个点到第$r$个点是安全的,同时

       对于某条边,如果他的两个端点都是安全的,那么这条边也是安全的。

       求在该限制条件下能互相安全到达的点对数。

    update:原来这个姿势叫做回滚莫队

    首先要做的就是分块,但是这道题的块的大小很难控制。

    从每个点开始按度数分块,保证每个块点的度数和约等于$blocksize$。

    然后就是依次处理每个块。假设当前块的左右边界分别为$l$和$r$。

    首先对所有边按照左端点(这里默认左端点小于右端点)升序排序。

    然后我们取出所有左端点在$[l, r]$内,右端点在$[r + 1, n]$内的询问。对这些询问按照右端点升序排序。

    每次处理一个询问的时候,确保那些左右端点都落在$[r + 1, n]$的边已经被添加到并查集(这部分用双指针维护,并且不能撤销)

    然后枚举那些左端点落在$[l, r]$的边,如果这条边对于当前询问来说是安全的那么添加到并查集。(这部分并查集的操作是要撤销的)

    对于那些左右端点在用一个块内的询问,直接暴力合并然后恢复即可。

    按照分块的策略,每个块里面左端点落在$[l, r]$的边的数量大概为$sqrt{m}$,这样乘上块的个数时间复杂度为$O(m)$

    然后还要带上启发式合并的复杂度,所以总的时间复杂度为$O(m^{frac{3}{2}}logn)$

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define rep(i, a, b)	for (int i(a); i <= (b); ++i)
    #define dec(i, a, b)	for (int i(a); i >= (b); --i)
    
    typedef long long LL;
    
    const int N = 1e5 + 10;
    
    struct node{
    	int x, y, id;
    	void scan(){
    		scanf("%d%d", &x, &y);
    		if (x > y) swap(x, y);
    	}
    
    } e[N], q[N], tmp[N];
    
    struct opt{
    	int x, y, sz, f;
    	LL ans;
    } op[N << 1];
    
    
    int T;
    int cnt, now, tot, n, m, qu, bs, l, r, top, pos;
    int belong[N], ed[N], in[N], sz[N], father[N];
    vector <node> v[N], g[N];
    LL  ret[N], ans;
    
    bool cmpx(const node &a, const node &b){ return a.x < b.x; }
    bool cmpy(const node &a, const node &b){ return a.y < b.y; }
    
    int calc(int x){
    	int l = 1, r = m;
    	if (e[1].x >= x) return 1;
    	if (e[m].x <  x) return m + 1;
    	
    	while (l + 1 < r){
    		int mid = (l + r) >> 1;
    		if (e[mid].x >= x) r = mid;
    		else l = mid + 1;
    	}
    
    	if (e[l].x >= x) return l;
    	else return r;
    }
    
    int getfather(int x){ return father[x] == x ? x : getfather(father[x]); }
    
    void solve(int x, int y){
    	int fx = getfather(x), fy = getfather(y);
    	if (fx == fy) return;
    	if (sz[fx] < sz[fy]) swap(fx, fy);
    
    	++top;
    	op[top].x   = fx;
    	op[top].y   = fy;
    	op[top].sz  = sz[fx];
    	op[top].f   = father[fy];
    	op[top].ans = ans;
    
    	father[fy]  = fx;
    	ans        += 1ll * sz[fx] * sz[fy];
    	sz[fx]     += sz[fy];
    }
    
    void undo(){
    	dec(i, top, 1){
    		int fx = op[i].x, fy = op[i].y;
    		father[fy] = op[i].f;
    		ans = op[i].ans;
    		sz[fx]  = op[i].sz;
    	}
    }
    
    int main(){
    
    	scanf("%d", &T);
    	while (T--){
    		scanf("%d%d%d", &n, &m, &qu);
    		rep(i, 1, m) e[i].scan();
    		rep(i, 1, qu) q[i].scan(), q[i].id = i;
    		sort(e + 1, e + m + 1, cmpx);
    
    		memset(in, 0, sizeof in);
    		rep(i, 1, m) ++in[e[i].x];
    		
    		bs  = (int)floor(sqrt((double)(2 * m) * (double)(log(n) / 0.4)));
    		tot = 0;
    
    		now = 0;
    		memset(ed, 0, sizeof ed);
    		rep(i, 1, n){
    			now += in[i];
    			if (now >= bs){
    				++tot;
    				belong[i] = tot;
    				ed[tot] = i;
    				now = 0;
    			}
    		}
    
    		if (ed[tot] != n){
    			++tot;
    			belong[n] = tot;
    			ed[tot] = n;
    		}
    
    		dec(i, n, 1) if (!belong[i]) belong[i] = belong[i + 1];
    
    		rep(i, 1, tot){
    			l = ed[i - 1] + 1, r = ed[i];
    			v[i].clear();
    			g[i].clear();
    			rep(j, 1, m)  if (e[j].x >= l && e[j].x <= r) v[i].push_back(e[j]);
    			rep(j, 1, qu) if (q[j].x >= l && q[j].y <= r) g[i].push_back(q[j]);
    		}
    
    		rep(et, 1, tot){
    			l = ed[et - 1] + 1, r = ed[et];
    
    			cnt = 0;
    			rep(i, 1, qu) if (q[i].x >= l && q[i].x <= r && q[i].y > r){
    				tmp[++cnt] = q[i];
    			}
    
    			sort(tmp + 1, tmp + cnt + 1, cmpy);
    			sort(e + 1, e + m + 1, cmpx);
    			pos = calc(r + 1);
    			sort(e + pos, e + m + 1, cmpy);
    
    			rep(i, 1, n) father[i] = i, sz[i] = 1;
    			ans = 0;
    			for (int j = pos, k = 1; k <= cnt; ++k){
    				while (e[j].y <= tmp[k].y && j <= m){
    					solve(e[j].x, e[j].y);
    					++j;
    				}
    
    				top = 0;
    				for (auto edge : v[et]){
    					if (tmp[k].x <= edge.x && edge.y <= tmp[k].y){
    						solve(edge.x, edge.y);
    					}
    				}
    
    				ret[tmp[k].id] = ans;
    				undo();
    			}
    
    			rep(i, 1, n) father[i] = i, sz[i] = 1;
    			ans = 0;
    
    			for (auto query : g[et]){
    				top = 0;
    				for (auto edge : v[et]){
    					if (query.x <= edge.x && edge.y <= query.y){
    						solve(edge.x, edge.y);
    					}
    				}
    				ret[query.id] = ans;
    				undo();
    			}
    
    		}
    		rep(i, 1, qu) printf("%lld
    ", ret[i]);
    	}
    
    	return 0;
    }
    

      

  • 相关阅读:
    js--事件
    js之table操作
    2019年目标
    history.back返回后输入框值丢失问题
    C++ 工程师养成 每日一题4.5 (迭代器遍历)
    C++ 工程师养成 每日一题fourth (reverse的使用)
    C++ 工程师养成 每日一题third (子数列排序)
    C++工程师养成 每日一题(string使用)
    C++工程师养成 每日一题(vector使用)
    运算符优先级
  • 原文地址:https://www.cnblogs.com/cxhscst2/p/8506744.html
Copyright © 2011-2022 走看看