P9869 [NOIP2023] 三值逻辑

原题链接​​​​​​​​​​​​​

中间的操作是不重要的,我们只关心最后的状态。一个点的最终状态要么确定,要么和另一个点的初始状态有关。可以开并查集合并一下,如果出现了确定的最终状态(赋值,或者 𝑥𝑖=¬𝑥𝑖)赋值即可。最终答案即为确定了必须为 𝑈 的点数。

这道题打暴力能打出来60pts的好成绩,hhh

60pts如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100005;
const int T = N, F = -N, U = 0; //三值逻辑
int c, t, n, m, a, b, fa[N];
char ch[N];
bool book[N];
int find(int x) {
	int re;
	if (x == T || x == F)re = x;
	else if (book[n - x] || x == U)re = U;
	else if (book[x + n])re = T;
	else if (x < 0) {
		if (x == -fa[-x])re = x;
		else {
			book[x + n] = 1;
			re = find(-fa[-x]);
			book[x + n] = 0;
		}
	}
	else {
		if (x == fa[x])re = x;
		else {
			book[x + n] = 1;
			re = fa[x] = find(fa[x]);
			book[x + n] = 0; //清空
		}
	}
	return re;
}
signed main() {
	cin >> c >> t;
	while (t--) {
		cin >> n >> m;
		for (int i = 1; i <= n; i++)fa[i] = i;
		for (int i = 1; i <= m; i++) {
			cin >> ch[i];
			if (ch[i] == 'T') {
				cin >> a;
				fa[a] = T;
			} else if (ch[i] == 'F') {
				cin >> a;
				fa[a] = F;
			} else if (ch[i] == 'U') {
				cin >> a;
				fa[a] = U;
			} else {
				cin >> a >> b;
				if (ch[i] == '+')fa[a] = fa[b];
				else fa[a] = -fa[b];
			}
		}
		int ans = 0;
		for (int i = 1; i <= n; i++) {
			if (find(i) == U){
				ans++;
			}
		}
		cout << ans << endl;
	}
	return 0;
}

满分代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
int C,T,a[200005],b[200005],fa[200005];char op[10];
int find(int x){
	return ((x==fa[x])?x:fa[x]=find(fa[x]));
}
void merge(int x,int y){
	if((x=find(x))==(y=find(y)))return;
	fa[x]=y;
}
void solve(){
	int n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=1,b[i]=i,a[i+n]=-1,b[i+n]=i;
	for(int i=1;i<=m;i++){
		scanf("%s",op);
		if(op[0]=='+'){
			int x=read(),y=read();
			a[x]=a[y],b[x]=b[y];
		}
		else if(op[0]=='-'){
			int x=read(),y=read();
			a[x]=-a[y],b[x]=b[y];
		}
		else{
			int x=read();
			if(op[0]=='T')a[x]=0,b[x]=-1;
			else if(op[0]=='F')a[x]=0,b[x]=-2;
			else a[x]=0,b[x]=-3;
		}
	}
	for(int i=1;i<=n+n;i++)fa[i]=i;
	for(int i=1;i<=n;i++){
		if(a[i]==0){
			a[find(i)]=a[i],b[find(i)]=b[i];
			if(b[i]!=-3)a[find(i+n)]=a[i],b[find(i+n)]=-3-b[i];
			else a[find(i+n)]=a[i],b[find(i+n)]=b[i];
		}
		else if(a[i]==1){
			if(find(i)==find(b[i]+n)){
				merge(i,b[i]);merge(i+n,b[i]);
				a[find(i)]=0,b[find(i)]=-3;
			}
			else merge(i,b[i]),merge(i+n,b[i]+n);
		}
		else{
			if(find(i)==find(b[i])){
				merge(i,b[i]+n);merge(i+n,b[i]+n);
				a[find(i)]=0,b[find(i)]=-3;				
			}
			else merge(i,b[i]+n),merge(i+n,b[i]);
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(a[find(i)]==0&&b[find(i)]==-3)ans++;
	}
	printf("%lld\n",ans);
}
signed main(){
	C=read(),T=read();
	while(T--){
		solve();
	}
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值