不会,又是\(lct\),三场连着考,我该学学了。。
还有好多知识点没学,联赛前还想多刷点思维题,,,,,,难受
扔个暴力吧,找个度大于等于三的做根,然后记录一个点的子树内是否有发射器,当某个点有多于\(1\)棵子树没有发射器时,设置发射器到只剩一个没有的子树
除了找根为啥找度大于等于三的都比较好理解
然后就是\(lct\)啥的维护,这里扔个暴力跑路
#include<cstring> #include<cstdio> #include<algorithm> #include<set> #include<vector> #include<queue> #include<random> #include<map> using namespace std; typedef long long ll; typedef unsigned long long ull; // mt19937 rd((ull)(new char) * (ull)(new char)); inline int read(){ int x = 0; char c = getchar(); while(c < '0' || c > '9')c = getchar(); do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0'); return x; } const int maxn = 500005; int n, q; int fa[maxn], ans, have[maxn], nohave[maxn]; bool flag[maxn]; vector<int>g[maxn]; void dfs(int x){ flag[x] = nohave[x] = have[x] = 0; for(int v : g[x]){ if(v == fa[x] || v == 0)continue; fa[v] = x; dfs(v); if(!flag[v]) ++nohave[x]; else ++have[x]; } if(nohave[x] > 1){ ans += nohave[x] - 1; flag[x] = 1; } if(have[x])flag[x] = 1; // printf("dfs:%d %d %d\n",x ,have[x], nohave[x]); } int rd[maxn]; void LINK(int u, int v){ g[u].push_back(v); g[v].push_back(u); ++rd[u]; ++rd[v]; } void CUT(int u, int v){ int su = g[u].size(); for(int i = 0; i < su; ++i)if(g[u][i] == v){g[u][i] = 0; break;} int sv = g[v].size(); for(int i = 0; i < sv; ++i)if(g[v][i] == u){g[v][i] = 0; break;} --rd[u]; --rd[v]; } void TREE(){ if(n == 1)ans = 0; else{ ans = 0; int root = -1; for(int i = 1; i <= n; ++i)if(rd[i] >= 3){root = i;break;} if(root == -1)ans = 1; else{fa[root] = 0; dfs(root);} } printf("%d\n",ans); } int main(){ freopen("location.in","r",stdin); freopen("location.out","w",stdout); n = read(); for(int i = 1; i < n; ++i){ int u = read(), v = read(); LINK(u, v); } TREE(); int q = read(); for(int i = 1; i <= q; ++i){ int u = read(), v = read(), x = read(), y = read(); CUT(u, v);LINK(x, y);TREE(); } return 0; }
分类处理,最小割只可能为\(0, 1 , 2 , 3\)
\(0\)不用管
\(1\)\(tarjan\)找桥
剩下的只能是\(2/3\)考虑如果\(mincut(a, b) == 3 mincut(b,c) == 3,mincut(a,c)\)一定不能是\(2\)那么最小割为\(3\)的构成了一些等价类,(或者说是集合?)
我们把搜索树搞出来,如果最小割为\(2\)那么要么是两个树边要么是一个树边一个非树边
我们给非树边一个随机数,树边设成经过他的非树边的异或和
如果一个树边和一个非树边相同,那么我们可以一个树边一个非树边割掉
如果两个树边相同,我们可以割掉他们(他们一定是树上的祖孙关系(连在一个点上下))
讲不太明白,自己画画图吧,代码还有一点注释
#include <bits/stdc++.h> #include <bits/extc++.h> using namespace std; using namespace __gnu_pbds; typedef long long ll; typedef unsigned long long ull; const int maxn = 1000005; const int maxm = 3000005; inline int read(){ int x = 0; char c = getchar(); while(c < '0' || c > '9')c = getchar(); do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0'); return x; } mt19937_64 rd((ull)(new char) * (ull)(new char)); int n, m, head[maxn], tot; struct edge{int to, net;}e[maxm << 1 | 1]; void add(int u, int v){ e[++tot].net = head[u]; head[u] = tot; e[tot].to = v; } struct SET{ int f[maxn]; void init(){for(int i = 1; i <= n; ++i)f[i] = i;} int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);} void merge(int u, int v){ u = fa(u); v = fa(v); if(u != v)f[v] = u; } }s; ll ans; int vis[maxn], dfn[maxn], low[maxn], tim, sta[maxn], top; int block, belong[maxn],size[maxn]; void tarjan(int x, int fa){ dfn[x] = low[x] = ++tim; sta[++top] = x; vis[x] = 1; for(int i = head[x]; i; i = e[i].net){ int v = e[i].to; if(v == fa)continue; if(!dfn[v]){ tarjan(v, x); low[x] = min(low[x], low[v]); }else if(vis[v])low[x] = min(low[x], dfn[v]); } if(dfn[x] == low[x]){ int si = 0, y; ++block; do{ ++si; y = sta[top--]; belong[y] = block; vis[y] = 0; }while(y != x); ans += 1ll * size[s.fa(x)] * si; size[s.fa(x)] += si; } } bool del[maxm], flag[maxn]; gp_hash_table<ull, bool>mp;//非树边颜色记录 gp_hash_table<ull, int>h, rcol; int now; ull col[maxn], val[maxn]; int dep[maxn], fa[maxn]; void dfs1(int x){//处理dep,fa基本信息,对非树边赋随机值进行树上差分 for(int i = head[x]; i; i = e[i].net) if(!del[i]){ int v = e[i].to; if(v == fa[x])continue; if(vis[v] != now){ dep[v] = dep[x] + 1; vis[v] = now; fa[v] = x; dfs1(v); }else if(dep[v] < dep[x]){ ull sr = rd(); mp[sr] = 1;//非树边 col[v] ^= sr; col[x] ^= sr; } } } void dfs2(int x){//处理树上差分,得到边的真实值 for(int i = head[x]; i; i = e[i].net) if(!del[i]){ int v = e[i].to; if(v == fa[x])continue; if(vis[v] != now){ vis[v] = now; dfs2(v); col[x] ^= col[v]; if(mp.find(col[v]) != mp.end())val[v] ^= rd();//如果与某个非树边颜色相同,那么可以通过割去该树边和非树边分开该点,这是一个新的等价类,并且该点单独成类 } } } void dfs3(int x){ if(fa[x] && rcol.find(col[x]) == rcol.end())rcol[col[x]] = x;//对树边颜色映射 for(int i = head[x]; i; i = e[i].net) if(!del[i]){ int v = e[i].to; if(v == fa[x])continue; if(vis[v] != now){ vis[v] = now; if(rcol.find(col[v]) != rcol.end()){//如果两个树边颜色相同,那么割掉这两个树边,能割掉该点,这是新的等价类 ull srd = rd(); val[rcol[col[v]]] ^= srd; val[v] ^= srd; } dfs3(v); } } } void dfs4(int x, ull nval){//分等价类 nval ^= val[x]; ++h[nval];//nval相同则为一个等价类 for(int i = head[x]; i; i = e[i].net) if(!del[i]){ int v = e[i].to; if(v == fa[x])continue; if(vis[v] != now){ vis[v] = now; dfs4(v, nval); } } } int main(){ freopen("juice.in","r",stdin); freopen("juice.out","w",stdout); n = read(); m = read(); s.init(); for(int i = 1; i <= m; ++i){ int u = read(), v = read(); add(u, v); add(v, u); s.merge(u, v); }//并查集维护连通性 for(int i = 1; i <= n; ++i)if(!dfn[i])tarjan(i, 0);//tarjan处理最小割为1的 for(int x = 1; x <= n; ++x) for(int i = head[x]; i; i = e[i].net) if(belong[x] != belong[e[i].to])del[i] = 1;//去掉桥 now = 0; for(int i = 1; i <= n; ++i) if(!flag[belong[i]]){ flag[belong[i]] = 1; mp.clear(); h.clear(); rcol.clear(); dep[i] = 0; fa[i] = 0; vis[i] = ++now; dfs1(i); vis[i] = ++now; dfs2(i); vis[i] = ++now; dfs3(i); vis[i] = ++now; dfs4(i, 0); ll las = 0; for(auto x : h){ ans += 3ll * x.second * (x.second - 1) / 2; ans += 2ll * las * x.second; las += x.second; } } printf("%lld\n",ans); return 0; }
关于树的直径的奇妙性质,
考场\(Possible\) - >\(possible\) \(41 - > 1\)我是**
大力分讨构造菊花,具体不想说了
#include<cstring> #include<cstdio> #include<algorithm> #include<set> #include<vector> #include<queue> #include<random> using namespace std; typedef long long ll; typedef unsigned long long ull; mt19937 rd((ull)(new char) * (ull)(new char)); const int maxn = 1000005; int n, p[maxn], cnt; bool vis[maxn]; void two(int u, int v){ if(n == 2){ printf("Possible\n"); printf("%d %d\n",1, 2); return; } int tov = 0, cv = 0, tou = 0,cu = 0, tno = 0; for(int i = 1;i <= n; ++i){ if(i == u || i == v)continue; if(p[i] == u)++cu; if(p[i] == v)++cv; } for(int i = 1; i <= n; ++i)if(p[i] == u && i != v){tou = i;break;} for(int i = 1; i <= n; ++i)if(p[i] == v && i != u){tov = i;break;} for(int i = 1; i <= n; ++i)if(p[i] == -1){tno = i;break;} if(tov == 0 && tou == 0){ if(n > 3){printf("Impossible\n");return;} int root = 0; for(int i = 1; i <= n; ++i)if(p[i] == -1){root = i; break;} printf("Possible\n"); printf("%d %d\n", root, u); printf("%d %d\n", root, v); return; } if(tov == 0 && tou != 0){printf("Impossible\n");return;} if(tov != 0 && tou == 0){printf("Impossible\n");return;} if(cv == 1 && cu == 1){ printf("Possible\n"); printf("%d %d\n", u, tov); printf("%d %d\n", v, tou); if(tno){ printf("%d %d\n", tno, tov); printf("%d %d\n", tno, tou); for(int i = 1; i <= n; ++i)if(p[i] == -1 && i != tno)printf("%d %d\n",i, tno); }else printf("%d %d\n",tou, tov); return; } if(cv == 1 && cu > 1){printf("Impossible\n");return;} if(cv > 1 && cu == 1){printf("Impossible\n");return;} printf("Possible\n"); printf("%d %d\n", u, tov); printf("%d %d\n", v, tou); int tu = 0, tv = 0; for(int i = 1; i <= n; ++i)if(p[i] == u && i != v && i != tou){tu = i; break;} for(int i = 1; i <= n; ++i)if(p[i] == v && i != u && i != tov){tv = i; break;} printf("%d %d\n", tv, tov); printf("%d %d\n", tu, tou); if(tno){ printf("%d %d\n", tno, tv); printf("%d %d\n", tno, tu); for(int i = 1; i <= n; ++i)if(p[i] == -1 && i != tno)printf("%d %d\n",i, tno); }else printf("%d %d\n",tu, tv); for(int i = 1; i <= n; ++i)if(p[i] == u && i != v && i != tou && i != tu)printf("%d %d\n",i, tu); for(int i = 1; i <= n; ++i)if(p[i] == v && i != u && i != tov && i != tv)printf("%d %d\n",i, tv); } vector<int>pu, p1; void onlyone(){ int u = 0; for(int i = 1; i <= n; ++i)if(vis[i]){u = i;break;} for(int i = 1; i <= n; ++i)if(p[i] == u)pu.push_back(i);else if(i != u)p1.push_back(i); if(pu.size() < 3 || p[u] != -1 || p1.size() == 0){printf("Impossible\n");return;} printf("Possible\n"); int su = pu.size(); for(int i = 1; i < su; ++i)printf("%d %d\n",pu[0], pu[i]); printf("%d %d\n",pu[0],p1[0]); int s1 = p1.size(); if(s1 > 1){ printf("%d %d\n",u, p1[1]); printf("%d %d\n",p1[0], p1[1]); for(int i = 2; i < s1; ++i)printf("%d %d\n",p1[i],p1[0]); }else printf("%d %d\n",u, p1[0]); } void work(){ if(cnt == 1){ if(n == 1 && p[1] == 1)printf("Possible\n"); else if(n == 1 && p[1] != 1)printf("Impossible\n"); else onlyone(); return; } for(int i = 1; i <= n; ++i)if(p[i] == i || p[i] > n){printf("Impossible\n");return;} if(cnt >= 3){printf("Impossible\n");return;} if(cnt == 0){ if(n <= 3)printf("Impossible\n"); else{printf("Possible\n"); for(int i = 2; i <= n; ++i)printf("%d %d\n", 1, i);} return; } if(cnt == 2){ int u = 0; for(int i = 1; i <= n; ++i)if(vis[i]){u = i; break;} int v = p[u]; if(v == -1){printf("Impossible\n");return;} if(p[v] == -1){printf("Impossible\n");return;} else two(u, v); } } int main(){ freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); scanf("%d",&n); for(int i = 1; i <= n; ++i)scanf("%d",&p[i]); for(int i = 1; i <= n; ++i) if(p[i] > 0){ if(!vis[p[i]])++cnt; vis[p[i]] = 1; } work(); return 0; }