[建图] [2SAT] [并查集] 题解:AT_agc059_c [AGC059C] Guessing Permutation for as Long as Possible

posted on 2025-03-18 03:44:47 | under | source

题意:有排列 \(p_1\dots p_n\),接下来提出 \(\frac {n(n-1)}2\) 次询问,每次 \((A_i,B_i)\) 即可得知 \(p_{A_i},p_{B_i}\) 的大小关系。已知每次询问前并不知道它俩的大小关系,求满足条件的排列个数。\(n\le 4\times 10^2\)

先判定。对排列建一张竞赛图,边 \(a\to b\) 表示 \(a<b\),那么某个时刻得知 \(u,v\) 大小关系,当前仅当存在两点间路径。

注意到该图满足传递性,那么只需考虑两条边构成的路径即可。对于较大的路径一定存在简化的两条边的路径。

因此对于询问 \((x,y)\),设存在 \((x,i),(i,y)\) 在此前询问,那么必须满足不能存在 \(p_x<p_i<p_y\)\(p_x>p_i>p_y\)

将大小关系视为点,用点的黑白描述大于还是小于。对于边 \((u,v,w)\),若 \(w=1\)\(u,v\) 大小关系相反反之相同。

不难发现一种合法的染色方案一定唯一存在一种排列与其对应。易证唯一性。对于存在性,假设不存在,意味着出现了环,但是在题目条件下环一定非法。因为考虑环上询问时间最晚的边,那么询问它时已经有路径存在了。

然后变成 2-SAT 问题求方案数,由于限制双向,所以答案即为 \(2\) 的连通块个数次幂。无解即为存在异或起来为 \(1\) 的环,用带权并查集维护即可。\(O(n^3)\)

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 4e2 + 5, M = 1e5 + 5, mod = 1e9 + 7;
int n, x, y, id[N][N], idd, ans, fa[M], faw[M];
bool vis[M], fl;

inline int find(int u){
	if(fa[u] == u) return u;
	int x = find(fa[u]); faw[u] ^= faw[fa[u]];
	return fa[u] = x;
}
inline void add(int x, int y, int w){
	int fx = find(x), fy = find(y);
	if(fx == fy){
		if(faw[x] ^ faw[y] ^ w) fl = true;
		return ; 
	}
	fa[fx] = fy, faw[fx] = w ^ faw[x] ^ faw[y];
}
signed main(){
	cin >> n;	
	for(int i = 1; i < n; ++i)
		for(int j = i + 1; j <= n; ++j) id[i][j] = id[j][i] = ++idd;
	for(int i = 1; i <= idd; ++i) fa[i] = i;
	for(int i = 1; i <= n * (n - 1) / 2; ++i){
		scanf("%d%d", &x, &y);
		for(int j = 1; j <= n; ++j){
			if(j == x || j == y) continue;
			int a = id[x][j], b = id[j][y];
			if(vis[a] && vis[b]) add(a, b, (x < j && j < y));
		}
		vis[id[x][y]] = true; 
	}
	if(fl) {puts("0"); return 0;}
	ans = 1;
	for(int i = 1; i <= idd; ++i) if(fa[i] == i) ans = 2ll * ans % mod;
	cout << ans;
	return 0;
}
posted @ 2026-01-12 20:14  Zwi  阅读(1)  评论(0)    收藏  举报