• 题解-APIO2019桥梁


    problem

    (mathrm {loj-3145})

    题意概要:给定一张 (n)(m) 边的无向图,边有边权,共 (q) 次操作,每次会将第 (x) 条边的权值改为 (y),或询问从 (x) 开始只走大于等于 (y) 的边能到达多少点。

    (nleq 5 imes 10^4, m,qleq 10^5)

    Solution

    这道题和 (HNOI2016) 最小公因数 长得很像,想到分块就会了。由于这题有修改,对询问的权值分块不好做,就只能对操作分块了。

    设操作块大小为 (T),则共 (lceilfrac qT ceil) 个操作快。

    对于每一个操作块,按照询问的权值大小顺序询问(否则就需要上可持久化并查集)。需要考虑两者的贡献:在块内被修改了的边、在块内未被修改的边。后者可以依照询问的权值大小依次添加,而由于目前将询问按权值大小排序,所以询问时间不一定递增,可以暴力处理块内修改,查看这条边是否有用。

    对于每一个询问,需要暴力扫描块内的修改,每次修改因为需要支持并查集撤销,复杂度 (O(Tlog n))。总共 (q) 个询问,共 (O(qTlog n))

    对于每一块而言,需要将所有边排序、块内询问排序,处理整块排序时会重构整个并查集,复杂度 (O(n+mlog m+Tlog T)),由于 (n,m,q) 同阶,所以简化为 (O(mlog m))。总共 (lceil frac qT ceil) 块,共 (O(frac qTcdot mlog m))

    考虑到 (n,m,q) 同阶,总复杂度为 (O(mTlog m+frac {m^2log m}T)=O(mlog m(T+frac mT))),取 (T=m^{frac 12}) 得复杂度最低为 (O(m^{frac 32}log m))。到这就能过了

    实际上没必要每一块内重新排序,可以将一开始排序后的序列拆出来,复杂度 (O(m)),重新计算复杂度得 (O(mTlog m+frac {m^2}T)),取 (T=sqrt {mlog m}) 可得 (O(m^{frac 32}sqrt {log m}))……但效率差别并不是很大。

    Code

    这里写的是前一个版本的。

    //loj-3145
    #include <bits/stdc++.h>
    using namespace std;
    
    template <typename _tp> inline void read(_tp&x){
    	char ch=getchar(),ob=0;x=0;
    	while(ch!='-'&&!isdigit(ch))ch=getchar();if(ch=='-')ob=1,ch=getchar();
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();if(ob)x=-x;
    }
    
    const int N = 101000;
    int n, m, Q;
    
    struct Edge {
    	int l, r, w, id;
    	inline void in() {read(l), read(r), read(w);}
    	friend inline bool operator < (const Edge&A, const Edge&B) {return A.w > B.w;}
    }e[N], ee[N];
    
    struct Qry {
    	int op, x, y, id;
    	inline void in() {read(op), read(x), read(y);}
    	friend inline bool operator < (const Qry&A, const Qry&B) {return A.y > B.y;}
    }q[N], md[N], qr[N];
    
    int d[N], sz[N];
    inline int f(int x) {while(d[x]) x = d[x]; return x;}
    
    struct RUB {int x, dx, y, sy;}Rub[N];
    int rub;
    
    inline void merge(int x, int y) {
    	x = f(x), y = f(y); if(x == y) return ;
    	if(sz[x] > sz[y]) swap(x, y);
    	Rub[++rub] = (RUB) {x, d[x], y, sz[y]};
    	d[x] = y, sz[y] += sz[x];
    }
    
    inline void cancel() {while(rub) d[Rub[rub].x] = Rub[rub].dx, sz[Rub[rub].y] = Rub[rub].sy, --rub;}
    
    int Ans[N], st[N], ew[N];
    bool ex[N];
    
    int main() {
    	read(n), read(m);
    	for(int i=1;i<=m;++i) e[i].in(), e[i].id = i;
    	int T = max(1.0, sqrt(m*log(m)/log(2)));
    	read(Q);
    	for(int l=1,r=T;l<=Q;l+=T) {
    		r = min(Q, l+T-1);
    		int t = r - l + 1, t0 = 0, t1 = 0;
    
    		for(int i=1;i<=m;++i) ex[i] = false;
    
    		for(int i=1;i<=t;++i) {
    			Ans[i] = 0, q[i].in(), q[i].id = i;
    			if(q[i].op == 1) md[++t0] = q[i], ex[q[i].x] = true;
    			else qr[++t1] = q[i];
    		}
    		sort(qr+1, qr+t1+1);
    		
    		for(int i=1;i<=n;++i) d[i] = 0, sz[i] = 1;
    
    		int em = 0, tp = 0;
    		for(int i=1;i<=m;++i)
    			if(!ex[i]) ee[++em] = e[i];
    			else st[++tp] = i;
    		sort(ee+1, ee+em+1);
    
    		int ie = 1;
    		for(int i=1;i<=t1;++i) {
    			while(ie <= em and ee[ie].w >= qr[i].y)
    				merge(ee[ie].l, ee[ie].r), ++ie;
    			
    			for(int j=1;j<=tp;++j) ew[st[j]] = e[st[j]].w;
    			for(int j=1;j<=t0 and md[j].id <= qr[i].id;++j)
    				ew[md[j].x] = md[j].y;
    			rub = 0;
    			for(int j=1;j<=tp;++j)
    				if(ew[st[j]] >= qr[i].y)
    					merge(e[st[j]].l, e[st[j]].r);
    			Ans[qr[i].id] = sz[f(qr[i].x)];
    			cancel();
    		}
    		for(int i=1;i<=t0;++i)
    			e[md[i].x].w = md[i].y;
    		for(int i=1;i<=t;++i)
    			if(Ans[i]) printf("%d
    ", Ans[i]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    【净界法师】【唯识三十颂05】《唯识三十颂》,什么是唯识呢?
    再谈呼吸调匀、全身放松之重要性
    何时该修十善,何时该念佛
    如何降服贪心
    用内观法切断欲望有何弊病
    洛桑陀美上师开示 | 如何遮止并断除贪爱?
    断除贪爱执着,才可以超越一切局限
    【佛子行浅释】逢遇悦意对境时, 视如夏季之彩虹, 虽显美妙然无实, 断除贪执佛子行。
    佛弟子如何断除对异性的贪爱与相思?益西彭措堪布超级精彩开示
    感人!北大博士出家前写给父母的信
  • 原文地址:https://www.cnblogs.com/penth/p/11382677.html
Copyright © 2020-2023  润新知