首页 > 其他分享 >洛谷 P9869 [NOIP 2023] 三值逻辑 题解

洛谷 P9869 [NOIP 2023] 三值逻辑 题解

时间:2023-11-19 11:35:50浏览次数:34  
标签:P9869 洛谷 cur 题解 双向 dfn low res 赋值

https://www.luogu.com.cn/problem/P9869?contestId=145259

看到要给变量赋初始值,还是 T, F, U 之类的,容易想到 2-SAT。

设 \(1 \sim n + m\) 的点表示 \(x_1, x_2, \dots, x_{n + m}\) 为 T 的点,其中 \(x_{k + n}(1 \leq k \leq m)\) 表示在第 \(k\) 次操作被操作的变量的值(操作后)。设 \(n + m + 1 \sim 2 \times (n + m)\) 表示 \(x_1, x_2, \dots, x_{n + m}\) 为 F 的点。

  • 如果给 \(x_i\) 赋值为 U,那么就是 \(i\) 和 \(i + n + m\) 连双向边。
  • 如果给 \(x_i\) 赋值为 T,那么就是 \(i + n + m\) 向 \(i\) 连单向边。
  • 如果给 \(x_i\) 赋值为 F,那么就是 \(i\) 向 \(i + n + m\) 连单向边。
  • 如果是 \(x_i\) 赋值为 \(x_j\),那么就是 \(j\) 决定了 \(i\),\(i\) 也可以倒推出 \(j\),\(cur_j\) 和 \(i\) 连双向边,\(cur_j + n + m\) 和 \(i + n + m\) 连双向边。
  • 如果是 \(x_i\) 赋值为 \(¬x_j\),那么就是 \(j\) 决定了 \(i\),\(i\) 也可以倒推出 \(j\),\(cur_j + n + m\) 和 \(i\) 连双向边,\(cur_j\) 和 \(i + n + m\) 连双向边。

声明:\(cur_i\) 为 当前操作 最后一次更新 \(i\) 的操作编号加 \(n\)。若没有则为 \(i\)。\(res_i\) 为最后一次更新 \(i\) 的操作编号加 \(n\)。若没有则为 \(i\)。

当然到最后 \(i(1 \leq i \leq n)\) 当然要和 \(x_{res_i}\) 相等,所以 \(i\) 和 \(x_{res_i}\) 连双向边,\(i + n + m\) 和 \(x_{res_i} + n + m\) 连双向边。

然后下一步就是跑 Tarjan 求 SCC,如果 \(res_i\) 和 \(res_i + n + m\) 在同一个 SCC 里说明第 \(i\) 个变量 TF 都不可以填,所以填 U。答案就是 \(scc_{res_i} = scc_{res_i + n + m}\) 的 \(i\) 的数量,时间复杂度 \(O(n + m)\)。

一是有思维定势 NOIP T2 肯定打不了 \(100\),二是觉得考场上做题和日常做题的感觉真的不一样。

有没有可能以后就是春季测试之类的,要学会灵活判断难度啊。

#include <bits/stdc++.h>
using namespace std;
bool Mbeqwq;
typedef long long ll;
inline int readqwq () {
	int x = 0, f = 0;
	char c = getchar ();
	for ( ; c < '0' || c > '9' ; c = getchar ()) f |= (c == '-');
	for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
	return (!f) ? (x) : (-x);
}
inline ll readllqwq () {
	ll x = 0, f = 0;
	char c = getchar ();
	for ( ; c < '0' || c > '9' ; c = getchar ()) f |= (c == '-');
	for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
	return (!f) ? (x) : (-x);
}
#define debug(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
const int N = 1e5 + 25;
int cur[N], dfn[N << 2], low[N << 2], scc[N << 2], ckqwq, cnt, vis[N << 2];
vector < int > g[N << 2];
stack < int > st;
inline void tarjan (int u) {
	dfn[u] = ++ ckqwq;
	low[u] = dfn[u];
	st.push (u), vis[u] = 1;
	for (auto v : g[u]) {
		if (!dfn[v]) {
			tarjan (v);
			low[u] = min (low[u], low[v]);
		}
		else if (vis[v]) low[u] = min (low[u], dfn[v]);
	}
	if (dfn[u] != low[u]) return ;
	cnt ++;
	do {
		int v = st.top ();
		st.pop ();
		vis[v] = 0;
		scc[v] = cnt;
		if (u == v) break;
		// debug ("scc[%d] <- %d\n", v, cnt);
	} while (true) ;
	return ;
}
inline void solveqwq () {
	int n = readqwq (), m = readqwq ();
	for (int i = 1;i <= n; ++ i) cur[i] = i;
	for (int i = 1;i <= 2 * (n + m); ++ i) g[i].clear (), vis[i] = 0;
	while (!st.empty ()) st.pop ();
	for (int i = n + 1;i <= n + m; ++ i) {
		char s[8];
		scanf ("%s", s + 1);
		int x = readqwq ();
		if (s[1] == 'U') {
			g[i].push_back (i + n + m);
			g[i + n + m].push_back (i);
		}
		else if (s[1] == 'T') {
			g[i + n + m].push_back (i);	
		}
		else if (s[1] == 'F') {
			g[i].push_back (i + n + m);
		}
		else if (s[1] == '+') {
			int y = readqwq ();
			y = cur[y];
			g[y].push_back (i);
			g[i].push_back (y);
			g[y + n + m].push_back (i + n + m);
			g[i + n + m].push_back (y + n + m);
		}
		else {
			int y = readqwq ();
			y = cur[y];
			g[y].push_back (i + n + m);
			g[i + n + m].push_back (y);
			g[i].push_back (y + n + m);
			g[y + n + m].push_back (i);
		}
		cur[x] = i;
	}
	for (int i = 1;i <= n; ++ i) {
	  if (cur[i] != i) {
	    g[i].push_back (cur[i]);
	    g[cur[i]].push_back (i);
	    g[i + n + m].push_back (cur[i] + n + m);
	    g[cur[i] + n + m].push_back (i + n + m);	    
	  }
	}
	for (int i = 1;i <= 2 * (n + m); ++ i) {
		dfn[i] = 0;
		low[i] = 0;
		scc[i] = 0;
	}
	ckqwq = 0;
	cnt = 0;
	for (int i = 1;i <= 2 * (n + m); ++ i) {
		if (!dfn[i]) tarjan (i);
	}
	// for (int i = 1;i <= n + m; ++ i) debug ("%d ", scc[i]); debug ("\n");
	// for (int i = 1;i <= n + m; ++ i) debug ("%d ", scc[i + n + m]); debug ("\n");
	int ans = 0;
	for (int i = 1;i <= n; ++ i) {
		int x = cur[i];
		int y = cur[i] + n + m;
		if (scc[x] == scc[y]) ans ++;
	}
	printf ("%d\n", ans);
	return ;
}
bool Medqwq;
signed main () {
//	debug ("%.8lf MB\n", (&Mbeqwq - &Medqwq) / 1048576.0);
	// freopen ("tribool.in", "r", stdin);
	// freopen ("tribool.out", "w", stdout);
	int c = readqwq ();
	int _ = readqwq ();
	while (_ --) {
		solveqwq ();
	}	
//	debug ("%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
	return 0;
}
// g++ tribool.cpp -o tribool -std=c++14 -O2 -Wall -Wextra -Wshadow -Wl,--stack=536870912 -D_GLIBCXX_DEBUG
// ulimit -s 536870912
// g++ tribool.cpp -o tribool -std=c++14 -O2 -Wall -Wextra -Wshadow -fsanitize=address,undefined,signed-integer-overflow,leak -D_GLIBCXX_DEBUG
/*
1 3
3 3
- 2 1
- 3 2
+ 1 3
3 3
- 2 1
- 3 2
- 1 3
2 2
T 2
U 2
*/

标签:P9869,洛谷,cur,题解,双向,dfn,low,res,赋值
From: https://www.cnblogs.com/RB16B/p/17841769.html

相关文章

  • qemu-kvm: error: failed to set MSR x38d to x0x 【问题解决】
    问题解决创建报错在下面的issues找到解决办法https://github.com/GNS3/gns3-server/issues/1774可以尝试在VM上禁用MSR,然后检查是否可以启动qemu计算机添加内核模块参数临时修改echoY>/sys/module/kvm/parameters/ignore_msrs或者永久修改cat>/etc/modp......
  • 洛谷 B2006 地球人口承载力估计(Python3)
    这题难点在理解题意。没有任何技术含量:(题目分析:1.“可持续发展”到底什么意思?Makeendsmeet.也就是说能养活的那些人一年消耗的等于地球一年产生的。2.题中为什么要给x,a,y,b?为了求等量关系。注意,这里"x 亿人生活 a 年,或供 y 亿人生活 b年"用的是地球新生的资源和原有......
  • AT_code_festival_2018_quala_b题解
    题意给定一个序列,里面的值只有可能是\(a\)或\(b\)(\(a<b\))。有\(m\)个区间,这里面的值必须是\(a\),求如何是序列总和最大。思路因为\(n\)和\(m\)都只有100,所以可以先暴力将所有值设为\(b\),再将区间里的值暴力修改为\(a\),最后统计答案。ACCODE#include<bits/stdc......
  • P9779 题解
    思路因为不一定是只有一个答案,也就是多选题。所以就转化成了在\(n\)个里面选若干个。而每种个数必须都试一次。所以答案为:\[\sum_{i=1}^{i\len}C_n^i\]\(C_n^m\)表示在\(n\)个里面选\(m\)个方案数,即组合问题。众所周知,\[2^n=\sum_{i=0}^{i\len}C_n^i\]而\(......
  • P9782 题解
    题意给定两个字符,分别是两个\(26\)进制数,\(A\)到\(Z\)分别表示\(0\)到\(25\)。求这两个字符的和。答案同样用这种\(26\)进制表示。不包含前导\(0\)。思路先转化成\(10\)进制,再转化成\(26\)进制即可。而因为只有一位所以就不用写循环,直接算出\(10\)进制下的和......
  • SP3889 Closest Number题解
    题意简述有两个\(n\)位十进制数\(a\),\(b\)。要将数字\(b\)的每一位重新排列后,使得得到的数字一个在大于等于\(a\)的情况下更接近\(a\),另一个在小于\(a\)的情况下更接近\(a\)。求这两个数,如果找不到就输出0。思路以大于等于\(a\)的为例。我们可以将\(b\)从小......
  • AT_gigacode_2019_b 题解
    本题考查基本语法。思路用while来枚举每一组数据,用if判断是否合法。在判断时需要使用逻辑运算符&&,它的意思是左右两个要求如果同时成立,则会返回true,否则返回false。\(a\gex\),\(b\gey\),\(a+b\gez\)。这三个条件都要同时成立,所以可以使用&&。ACCODE#include......
  • SP3881 题解
    前置知识最短路。思路这就是一道很简单的最短路板子,太良心了,用堆优化的Dijkstra就能过。相信大家都会这个,我就不介绍了。ACCODE#include<bits/stdc++.h>usingnamespacestd;intdis[100005],n,m,s,t,vis[100005];vector<pair<int,int>>e[100005];inlinevoiddijkst......
  • P7907 [Ynoi2005] rmscne 题解
    P7907[Ynoi2005]rmscne题解退役前的最后一篇题解,献给Ynoi。再见了各位。题目大意给定一个长度为\(n\)的序列和\(m\)次查询,对于每次查询,给定\(l,r\),求出一个最短的子区间\([l',r']\),满足所有在区间\([l,r]\)中出现的数也在\([l',r']\)中出现过。题解题还是很......
  • P3412 仓鼠找sugar II 题解
    P3412仓鼠找sugarII题解大水题一个题目大意给定你一个树,设\(f_{u,v}\)表示在树上随机游走的情况下从\(u\)走到\(v\)的期望步数,求\(\displaystyle\frac{\sum_{i=1}^n\sum_{j=1}^nf_{i,j}}{n^2}\)。题解不难想到dp,不过\(1e5\)的范围差点让我怀疑我\(O(n......