#C240812B.「NOIP2023」三值逻辑


#C240812B.「NOIP2023」三值逻辑

标签(Label)

  • 图论转化
  • 深搜

前言(Front talk)

$\qquad$去年没有做出来主要是两个问题:1.考场上太急,没有认真分析就开始做;2.没有细心模拟样例。

$\qquad$这道题确实是转化为图论来做,但是由于没有分析好样例,导致我那时抓着半截就走,从一开始方向就错了。

网址(Website)

洛谷spoj

题目(Problem)

题目描述

小 L 今天学习了 Kleene 三值逻辑。

在三值逻辑中,一个变量的值可能为:真($\mathit{True}$,简写作 $\mathit{T}$)、假($\mathit{False}$,简写作 $\mathit{F}$)或未确定($\mathit{Unknown}$,简写作 $\mathit{U}$)。

在三值逻辑上也可以定义逻辑运算。由于小 L 学习进度很慢,只掌握了逻辑非运算 $\lnot$,其运算法则为:
$$\lnot \mathit{T} = \mathit{F}, \lnot \mathit{F} = \mathit{T}, \lnot\mathit{U} = \mathit{U}.$$

现在小 L 有 $n$ 个三值逻辑变量 $x_1,\cdots, x_n$。小 L 想进行一些有趣的尝试,于是他写下了 $m$ 条语句。语句有以下三种类型,其中 $\leftarrow$ 表示赋值:

  1. $x_i \leftarrow v$,其中 $v$ 为 $\mathit{T}, \mathit{F}, \mathit{U}$ 的一种;
  2. $x_i \leftarrow x_j$;
  3. $x_i \leftarrow \lnot x_j$。

一开始,小 L 会给这些变量赋初值,然后按顺序运行这 $m$ 条语句。

小 L 希望执行了所有语句后,所有变量的最终值与初值都相等。在此前提下,小 L 希望初值中 $\mathit{Unknown}$ 的变量尽可能少。

在本题中,你需要帮助小 L 找到 $\mathit{Unknown}$ 变量个数最少的赋初值方案,使得执行了所有语句后所有变量的最终值和初始值相等。小 L 保证,至少对于本题的所有测试用例,这样的赋初值方案都必然是存在的。

输入格式

本题的测试点包含有多组测试数据。

输入的第一行包含两个整数 $c$ 和 $t$,分别表示测试点编号和测试数据组数。对于样例,$c$ 表示该样例与测试点 $c$ 拥有相同的限制条件。

接下来,对于每组测试数据:

  • 输入的第一行包含两个整数 $n$ 和 $m$,分别表示变量个数和语句条数。
  • 接下来 $m$ 行,按运行顺序给出每条语句。
    • 输入的第一个字符 $v$ 描述这条语句的类型。保证 $v$ 为 TFU+- 的其中一种。
    • 若 $v$ 为 TFU 的某一种时,接下来给出一个整数 $i$,表示该语句为 $x_i \leftarrow v$;
    • 若 $v$ 为 +,接下来给出两个整数 $i,j$,表示该语句为 $x_i \leftarrow x_j$;
    • 若 $v$ 为 -,接下来给出两个整数 $i,j$,表示该语句为 $x_i \leftarrow \lnot x_j$。

输出格式

对于每组测试数据输出一行一个整数,表示所有符合条件的赋初值方案中,$\mathit{Unknown}$ 变量个数的最小值。

样例 #1

样例输入 #1

1 3
3 3
- 2 1
- 3 2
+ 1 3
3 3
- 2 1
- 3 2
- 1 3
2 2
T 2
U 2

样例输出 #1

0
3
1

提示

【样例解释 #1】

第一组测试数据中,$m$ 行语句依次为

  • $x_2 \leftarrow \lnot x_1$;
  • $x_3 \leftarrow \lnot x_2$;
  • $x_1 \leftarrow x_3$。

一组合法的赋初值方案为 $x_1 = \mathit{T}, x_2 = \mathit{F}, x_3 = \mathit{T}$,共有 $0$ 个$\mathit{Unknown}$ 变量。因为不存在赋初值方案中有小于 $0$ 个$\mathit{Unknown}$ 变量,故输出为 $0$。

第二组测试数据中,$m$ 行语句依次为

  • $x_2 \leftarrow \lnot x_1$;
  • $x_3 \leftarrow \lnot x_2$;
  • $x_1 \leftarrow \lnot x_3$。

唯一的赋初值方案为 $x_1 = x_2 = x_3 = \mathit{U}$,共有 $3$ 个$\mathit{Unknown}$ 变量,故输出为 $3$。

第三组测试数据中,$m$ 行语句依次为

  • $x_2 \leftarrow \mathit{T}$;
  • $x_2 \leftarrow \mathit{U}$;

一个最小化 $\mathit{Unknown}$ 变量个数的赋初值方案为 $x_1 = \mathit{T}, x_2 = \mathit{U}$。$x_1 = x_2 = \mathit{U}$ 也是一个合法的方案,但它没有最小化 $\mathit{Unknown}$ 变量的个数。

【样例解释 #2】

该组样例满足测试点 $2$ 的条件。

【样例解释 #3】

该组样例满足测试点 $5$ 的条件。

【样例解释 #4】

该组样例满足测试点 $8$ 的条件。

【数据范围】

对于所有测试数据,保证:

  • $1 \le t \le 6$,$1 \le n,m \le 10 ^ 5$;
  • 对于每个操作,$v$ 为 TFU+- 中的某个字符,$1 \le i,j \le n$。
测试点编号$n,m\leq$$v$ 可能的取值
$1,2$$10$$\mathit{TFU+-}$
$3$$10^3$$\mathit{TFU}$
$4$$10^5$$\mathit{TFU}$
$5$$10^3$$\mathit{U+}$
$6$$10^5$$\mathit{U+}$
$7$$10^3$$\mathit{+-}$
$8$$10^5$$\mathit{+-}$
$9$$10^3$$\mathit{TFU+-}$
$10$$10^5$$\mathit{TFU+-}$

题解(Solution)

$\qquad$分析样例以及数据范围,可以发现每个值最终的结果只和最后一次操作有关,这个地方很容易误导我们直接倒着做,但是中间被覆盖的操作虽然对当前位置没有影响,却会影响到其他的位置,因此,到这座反而更加的麻烦。

$\qquad$考虑正向分析,这里用了样例二中的第二组数据:

1 1
10 10
- 7 6
+ 4 1
+ 6 4
T 1
+ 2 9
- 9 10
U 10
+ 5 5
U 8
T 3

$\qquad$注意图中的 $x_6$ 位置,由于其被赋值为 $x_4$ ,而此时的 $x_4$ 的值为 $x1$ ,因此我们可以直接将其赋值为 $x_1$ 。可见,最终每个 $x_i$ 值只有 $5$ 中情况:$T$、$F$、$U$、$x_j$、$!x_j$ 。

$\qquad$发现位置之间的关系可以转化为图上点与点之间的关系,于是我们考虑依照最后的关系建无向图,建出来的图最终会是几个连通块的形式。对于每个连通块,如果里面存在 $T$ 、$F$ 中的任意一个,那么这个图中的所有点都一定是 $T$ 、$F$;如果存在 $U$ ,那么所有的点都是 $U$ ;如果都没有,那么我们可以尝试令其中任意一个点为 $T$ ,若最终没有矛盾,那么这个连通块里面的所有点都可以成为$T$或$F$,否则只能全部为 $U$ 。因为题目保证一定有解,出现$T$ 、$F$的联通块中一定不会有$U$,且出现$U$的联通块中一定不会有$T$ 、$F$。

$\qquad$时间复杂度:$O(n+m)$

$\qquad$后补:对于如何建边,以下标为 $x,y$ ,是否取反为边权 $v$ ,搜索时只需要看边权 $v$ ,就知道 $x$ 、$y$ 两点是相同还是相反了。

代码(Code)

#include<bits/stdc++.h>
#include<vector> 
#define For(i,l,r) for(int i=l;i<=r;i++)
#define Rof(i,l,r) for(int i=l;i>=r;i--)
using namespace std;
#define P pair<int,int>
#define int long long
#define fi first
#define se second
inline int rd(){int x;return cin>>x,x;}
const int inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2012345;

vector<P> ft[N];
int n,m,ans,a[N]; 

#define T (n+1)
#define F (n+2)
#define U (n+3)
inline int f(int x){
	if(x==T) return F;else
	if(x==F) return T;else
	if(x==U) return U;
	else return -x;
}

bitset<N> vst;
int siz[N];
inline void dfs_col(int x){
	siz[x] = vst[x] = true;
	for(auto p:ft[x]){
		int y = p.fi, v = p.se;
		if(a[y]) assert(v?a[x]==a[y]:a[x]==f(a[y]));
		else{
			a[y] = v ? a[x] : f(a[x]);
			dfs_col(y);
			siz[x] += siz[y];
		}
	}
}

inline bool dfs_nxt(int x){
	siz[x] = vst[x] = true;
	bool flg = false;
	for(auto p:ft[x]){
		int y = p.fi, v = p.se;
		if(a[y]){
			int chk = v ? a[x] : f(a[x]);
			if(chk!=a[y]) flg = true;
		}else{
			a[y] = v ? a[x] : f(a[x]);
			flg |= dfs_nxt(y);
			siz[x] += siz[y];
		}
	}return flg;
}

//多测清空 
signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int Tim = clock(), tid=rd(), TT=rd();
	cerr<<"TID:"<<tid<<'\n';
	while(TT--){
		cin>>n>>m;
		
		fill(siz,siz+n+1,0);vst.reset();
		fill(a,a+n+1,0);ans = 0;
		For(i,1,n) ft[i].clear();
		
		while(m--){
			char v;cin>>v;
			if(v=='+'||v=='-'){
				int i,j;cin>>i>>j;
				if(a[j]) a[i] = v=='+' ? a[j] : f(a[j]);
				else	 a[i] = v=='+' ? j : -j;
			}else{
				int i = rd();
				switch(v){
					case 'T':{a[i] = T;break;}
					case 'F':{a[i] = F;break;}
					case 'U':{a[i] = U;break;}
				}
			}
		}For(i,1,n) cerr<<a[i]<<' ';cerr<<'\n';
		//以点建边 
		For(i,1,n){
			int x = i, y = abs(a[i]);
			if(1<=y && y<=n){
				if(a[i]>0){
					ft[x].emplace_back(y,1);
					ft[y].emplace_back(x,1);
				}else
				if(a[i]<0){
					ft[x].emplace_back(y,0);
					ft[y].emplace_back(x,0);
				}a[i] = 0;//将颜色调为 0  
			}
		}
		
		For(x,1,n) if(a[x]>n && !vst[x])  dfs_col(x), ans += a[x]==U?siz[x]:0;
		For(x,1,n) if(a[x]<=n){
			a[x]=T;
			if(dfs_nxt(x)) 
				ans += siz[x];	
		}
		
		cout<<ans<<'\n';
	}return cerr<<"TIME:"<<(clock()-Tim)/1000.,0;
}

文章作者: WolfDeer
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 WolfDeer !
  目录