A层省选3

A. 定位系统

不会,又是\(lct\),三场连着考,我该学学了。。

还有好多知识点没学,联赛前还想多刷点思维题,,,,,,难受

扔个暴力吧,找个度大于等于三的做根,然后记录一个点的子树内是否有发射器,当某个点有多于\(1\)棵子树没有发射器时,设置发射器到只剩一个没有的子树

除了找根为啥找度大于等于三的都比较好理解

然后就是\(lct\)啥的维护,这里扔个暴力跑路

LINK-CUT-TREE
#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\)那么要么是两个树边要么是一个树边一个非树边

我们给非树边一个随机数,树边设成经过他的非树边的异或和

如果一个树边和一个非树边相同,那么我们可以一个树边一个非树边割掉

如果两个树边相同,我们可以割掉他们(他们一定是树上的祖孙关系(连在一个点上下))

讲不太明白,自己画画图吧,代码还有一点注释

code
#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;
}

C. 卷王

关于树的直径的奇妙性质,

考场\(Possible\) - >\(possible\) \(41 - > 1\)我是**

大力分讨构造菊花,具体不想说了

code
#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;
}
posted @ 2022-08-12 21:23  Chen_jr  阅读(26)  评论(0编辑  收藏  举报