NOIP2023T2 有人和我做法一样吗
  • 板块学术版
  • 楼主Alex_wcq
  • 当前回复1
  • 已保存回复1
  • 发布时间2023/11/18 22:51
  • 上次更新2023/11/19 09:36:01
查看原帖
NOIP2023T2 有人和我做法一样吗
482049
Alex_wcq楼主2023/11/18 22:51

先算出每个数最后变成了啥,然后搜索算出哪些数必须是U/T/F。

对于剩下的部分,处理出必须相等的连通块,然后以这些连通块为点,必须为相反的连边,建一张新图。

对于新图中的每个连通块,如果有奇圈就必须全是U,否则就可以全是T/F。

然后我不会判奇圈,于是把它的BFS树建出来,有奇圈等价于有两端深度相同的边。

我也不知道我考场为什么把BFS树上的边存下来了。

考场代码,民间数据都过了。

#include<bits/stdc++.h>
using namespace std;
int read(){
	char c=getchar(); int a=0;
	while(c>'9'||c<'0') c=getchar();
	while(c<='9'&&c>='0'){
		a=a*10+(c-'0');
		c=getchar();
	}
	return a;
}
int ans,n,v[100010],iv[100010]; //x_i=i,U=-1,T=-2,F=-3 
void f1(){ //读入,并初步处理操作 
	int m; n=read(); m=read();
	for(int i=1;i<=n;++i){
		v[i]=i;
		iv[i]=0;
	}
	while(m--){
		char c=getchar();
		while(c==' '||c=='\n') c=getchar();
		if(c=='+'||c=='-'){
			int x,y; x=read(); y=read(); v[x]=v[y];
			if(c=='+') iv[x]=iv[y];
			else iv[x]=1-iv[y];
		}
		else{
			int x; x=read(); iv[x]=0;
			if(c=='U') v[x]=-1;
			else if(c=='T') v[x]=-2;
			else v[x]=-3;	
		} 
	}
} 
vector<int> gx[100010]; int fl[100010]; //无=0,U=1,TF=2 
void f2(){ //标记直接确定是UTF的
	queue<int> q;
	for(int i=1;i<=n;++i)
		if(v[i]>0) gx[v[i]].push_back(i);
		else{
			q.push(i);
			if(v[i]==-1) fl[i]=1;
			else fl[i]=2;
		}
	while(!q.empty()){
		int u=q.front(); q.pop();
		for(int v:gx[u]){
			if(fl[v]!=0) continue;
			fl[v]=fl[u]; q.push(v);
		}
	}
	for(int i=1;i<=n;++i)
		if(fl[i]==1) ++ans;
}
//并查集
int fa[100010];
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
} 
void merge(int x,int y){
	x=find(x); y=find(y);
	if(x!=y) fa[x]=y;
}
void init(){
	for(int i=1;i<=n;++i) fa[i]=i;
}
int ki[100010],ks[100010];
void f3(){ //建立+的连通块
	init();
	for(int i=1;i<=n;++i)
		if(fl[i]==0&&iv[i]==0) merge(i,v[i]);
	for(int i=1;i<=n;++i)
		if(fl[i]==0){
			ki[i]=find(i);
			ks[ki[i]]++;
		}
}
vector<int> ed[100010],dj[100010];
void f4(){ //建-的图,找连通块
	init();
	for(int i=1;i<=n;++i)
		if(fl[i]==0&&iv[i]==1){
			ed[ki[i]].push_back(ki[v[i]]); ed[ki[v[i]]].push_back(ki[i]);
			merge(ki[i],ki[v[i]]);
		}
	for(int i=1;i<=n;++i)
		dj[find(i)].push_back(i);
}
vector<int> e[100010]; int dep[100010]; 
void f5(vector<int> pt){ //建立BFS树,预处理dep 
	queue<int> q; q.push(pt[0]); dep[pt[0]]=1;
	while(!q.empty()){
		int u=q.front(); q.pop();
		for(int v:ed[u]){
			if(dep[v]) continue;
			dep[v]=dep[u]+1; q.push(v);
			e[u].push_back(v);
		}
	}
} 
bool f6(vector<int> pt){ //通过找是否有横插边判断是否为二分图 
	for(int u:pt)
		for(int v:ed[u])
			if(dep[u]==dep[v]) return 1;
	return 0;
}
void f7(){ //对每个连通块做 f5,f6,并求答案 
	for(int i=1;i<=n;++i){
		if(dj[i].empty()) continue;
		f5(dj[i]);
		if(!f6(dj[i])) continue;
		for(int x:dj[i]) ans+=ks[x];
	}
}
#define m(x) memset(x,0,sizeof(x))
#define c(x) for(int i=0;i<=100000;++i) x[i].clear()
void f0(){ //多测清零 
	ans=n=0; 
	m(v); m(iv); m(fl); c(gx); m(fa); m(ki); m(ks);
	c(ed); c(dj); c(e); m(dep);
}
#undef m
#undef c
int main(){
	freopen("tribool.in","r",stdin);
	freopen("tribool.out","w",stdout);
	int c,T; scanf("%d%d",&c,&T);
	while(T--){
		f0(); f1(); f2(); f3(); f4(); f7();
		printf("%d\n",ans); 
	}
	return 0;
}
2023/11/18 22:51
加载中...