CF576E Painting Edges 题解
CF576E Painting Edges
关于我看完题解后改了一个位置就过题导致我都不知道我怎么过的
当然现在真的大彻大悟了。
题意
给定一张
思路
首先观察到颜色数非常少,可以考虑对每个点都开
吗?
我们会发现这道题有一个不一样的地方:有些操作是无法执行的,这导致我们一开始挂上线段树的操作可能需要撤回。
显然,直接删除是不现实的。我们可以将删除改为维持原状,即把不合法的修改变成上一个合法修改。
然后,我们发现,对于每一个时间点我我们都只对应一个操作,这一个性质非常重要。我们对线段树 dfs 的时候,也是按照时间顺序去访问每个叶子节点的,也就是说,当我们访问到一个叶子节点的时候,会把之前的所有合法操作执行完,同时可以知道这个时刻对应的操作!
同样因为时序性,每个叶子节点的操作都应该最后被执行,那我们就可以考虑把第
这样,我们的问题就解决了。每次遍历到一个叶子节点,先进行判断,如果不合法,就把这个操作改成上一次操作后的颜色。
代码:
#include<bits/stdc++.h> #define ls tr<<1 #define rs tr<<1 | 1 using namespace std; const int N = 5e5+100; inline int read(){ int x = 0; char ch = getchar(); while(ch<'0' || ch>'9') ch = getchar(); while(ch>='0'&&ch<='9') x = x*10+ch-48, ch = getchar(); return x; } struct Edge{ int u, v; }e[N]; struct Node{ int tk, c; }op[N]; struct Revoke{ int c, u, del; }; stack<Revoke> s; int top; int fa[55][N<<1], dep[55][N<<1]; int n, m, K, Q; vector<int> tree[N<<2]; void insert(int tr, int L, int R, int lq, int rq, int tk){ if(L == lq && rq == R){ tree[tr].push_back(tk); return; } int mid = (L+R)>>1; if(lq<=mid) insert(ls, L, mid, lq, min(rq, mid), tk); if(rq> mid) insert(rs, mid+1, R, max(lq, mid+1), rq, tk); } inline int find(int x, int col){ while(x!=fa[col][x]) x = fa[col][x]; return x; } void merge(int col, int x, int y){ int fx = find(x, col), fy = find(y, col); if(fx == fy) return; if(dep[col][fx]>dep[col][fy]) swap(fx, fy); ++top; Revoke tmp = (Revoke){col, fx, dep[col][fx] == dep[col][fy]}; s.push(tmp); fa[col][fx] = fy; dep[col][fy]+=(dep[col][fx] == dep[col][fy]); } bool ans[N]; int lstcol[N]; void solve(int tr, int L, int R){ int last = s.size(); for(int i = 0; i<tree[tr].size(); ++i){ int opi = tree[tr][i]; if(!op[opi].c) continue;//如果没有颜色就不进行修改 int u = e[op[opi].tk].u, v = e[op[opi].tk].v; int col = op[opi].c; merge(col, u, v+n); merge(col, v, u+n); } if(L == R){//当我们遍历到叶子节点的时候,这个时刻对应的操作是未被执行的 //而之前所有的合法操作都已经执行完毕,因此这时候是可以判断该次操作是否合法 int col = op[L].c, tk = op[L].tk; int x = e[tk].u, y = e[tk].v; if(find(x, col) == find(y, col)){ puts("NO"); op[L].c = lstcol[tk];//如果不合法,直接把这次操作改为上次操作的颜色 } else{ puts("YES"); lstcol[tk] = op[L].c;//记录最后一次合法修改的颜色 } } else{ int mid = (L+R)>>1; solve(ls, L, mid); solve(rs, mid+1, R); } while(s.size()>last){ Revoke tmp = s.top(); s.pop(); int col = tmp.c, u = tmp.u, del = tmp.del; dep[col][fa[col][u]]-=del; fa[col][u] = u; } } int las[N]; int tot; int main(){ n = read(), m = read(), K = read(), Q = read(); for(int i = 1; i<=m; ++i){ e[i].u = read(), e[i].v = read(); } for(int i = 1; i<=Q; ++i){ op[i].tk = read(), op[i].c = read(); if(las[op[i].tk] && las[op[i].tk]<i-1){ insert(1, 1, Q, las[op[i].tk]+1, i-1, las[op[i].tk]); } las[op[i].tk] = i; } for(int i = 1; i<=m; ++i){ if(las[i]<Q){ insert(1, 1, Q, las[i]+1, Q, las[i]); } } for(int i = 1; i<=K; ++i){ for(int j = 1; j<=n*2; ++j){ fa[i][j] = j; } } solve(1, 1, Q); return 0; }