題意概要:給定一張 \(n\) 點 \(m\) 邊的無向圖,邊有邊權,共 \(q\) 次操做,每次會將第 \(x\) 條邊的權值改成 \(y\),或詢問從 \(x\) 開始只走大於等於 \(y\) 的邊能到達多少點。git
\(n\leq 5\times 10^4,\ m,q\leq 10^5\)spa
這道題和 \(HNOI2016\) 最小公因數 長得很像,想到分塊就會了。因爲這題有修改,對詢問的權值分塊很差作,就只能對操做分塊了。code
設操做塊大小爲 \(T\),則共 \(\lceil\frac qT \rceil\) 個操做快。排序
對於每個操做塊,按照詢問的權值大小順序詢問(不然就須要上可持久化並查集)。須要考慮二者的貢獻:在塊內被修改了的邊、在塊內未被修改的邊。後者能夠依照詢問的權值大小依次添加,而因爲目前將詢問按權值大小排序,因此詢問時間不必定遞增,能夠暴力處理塊內修改,查看這條邊是否有用。get
對於每個詢問,須要暴力掃描塊內的修改,每次修改由於須要支持並查集撤銷,複雜度 \(O(T\log n)\)。總共 \(q\) 個詢問,共 \(O(qT\log n)\)。it
對於每一塊而言,須要將全部邊排序、塊內詢問排序,處理整塊排序時會重構整個並查集,複雜度 \(O(n+m\log m+T\log T)\),因爲 \(n,m,q\) 同階,因此簡化爲 \(O(m\log m)\)。總共 \(\lceil \frac qT\rceil\) 塊,共 \(O(\frac qT\cdot m\log m)\)。io
考慮到 \(n,m,q\) 同階,總複雜度爲 \(O(mT\log m+\frac {m^2\log m}T)=O(m\log m(T+\frac mT))\),取 \(T=m^{\frac 12}\) 得複雜度最低爲 \(O(m^{\frac 32}\log m)\)。到這就能過了class
實際上不必每一塊內從新排序,能夠將一開始排序後的序列拆出來,複雜度 \(O(m)\),從新計算複雜度得 \(O(mT\log m+\frac {m^2}T)\),取 \(T=\sqrt {m\log m}\) 可得 \(O(m^{\frac 32}\sqrt {\log m})\)……但效率差異並非很大。效率
這裏寫的是前一個版本的。
//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\n", Ans[i]); } return 0; }