[NOI2017] 游戏

前言

这道题把U群搞得很热闹,顺便教育了一波自以为卡常的菜逼(我)。

题目

洛谷(普通版)

UOJ(炼狱版)

讲解

普通版

我们发现虽然总共有 3 种车可以选,但是每个地图有 1 种车不能选,所以排除掉离谱的 3-SAT 之后,还是一个比较简单的 2-SAT 板题。

一个比较 naive 的想法是枚举 x 处选什么车,这样复杂度是 \(O(3^dn)\) 的,应该在哪里都过不了,说不定能过原始数据。

但实际上我们只需要枚举 x 处选什么地图,根据鸽巢原理,我们发现任意两个地图就可以包含三种车,所以复杂度变成 \(O(2^dn)\),普通版可过。

炼狱版

WARNING:前方高能,非战斗人员请迅速撤离!

但是普通版的代码交到 UOJ 上是过不了的,我的代码会 T 在 extest12 上,而且我们一个机房都过不了。

于是我就去 U 群里面问

EI 表示是卡常(此时我以为正解就是c=2)

然后事情就不对劲起来了

其中混进了一只萌萌的兔(蛙)队

随后 $哥哥 给出做法

最后一个我不认识的大佬给出补充

大家都看懂了吧,那我就不讲了,所以复杂度就是 \(O(1.5^dn)\)

代码

普通版
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std; 

typedef long long LL;
const int MAXN = 50005 << 1;
int n,m;
char c[MAXN];

LL Read()
{
	LL x = 0,f = 1;char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

char gc(){
	char c = getchar();
	while(c > 'D' || c < 'A') c = getchar();
	return c;
}

vector<int> arb;
int head[MAXN],tot;
struct edge{
	int v,nxt;
}e[MAXN<<1];
void Add_Edge(int u,int v){
	e[++tot] = edge{v,head[u]};
	head[u] = tot;
}

int cs[MAXN][2],E[MAXN][4];
void Get(int i,char s){
	if(s == 'a') cs[i][0] = 1,cs[i][1] = 2;
	else if(s == 'b') cs[i][0] = 0,cs[i][1] = 2;
	else if(s == 'c') cs[i][0] = 0,cs[i][1] = 1;
}
int Find(int x,int s){
	if(cs[x][0] == s) return 0;
	else if(cs[x][1] == s) return 1;
	return 2;
}
int dfn[MAXN],dfntot,bl[MAXN],qlt,low[MAXN],st[MAXN],tl;
bool ins[MAXN];
void Tarjan(int x){
	ins[x] = 1; st[++tl] = x;
	dfn[x] = low[x] = ++dfntot;
	for(int i = head[x],v; i ;i = e[i].nxt){
		v = e[i].v;
		if(!dfn[v]) Tarjan(v),low[x] = Min(low[x],low[v]);
		else if(ins[v]) low[x] = Min(low[x],dfn[v]);
	}
	if(dfn[x] == low[x]){
		int v; bl[x] = ++qlt;
		do{
			bl[v = st[tl--]] = qlt;
			ins[v] = 0;
		}while(v ^ x);
	}
}
void solve(){
	for(int i = (n<<1);i >= 1;-- i) dfn[i] = head[i] = bl[i] = ins[i] = 0;
	tl = qlt = tot = dfntot = 0;
	for(int i = 1,A,B;i <= m;++ i){
		if((A = Find(E[i][0],E[i][1])) > 1) continue;
		B = Find(E[i][2],E[i][3]);
		if(B > 1) Add_Edge(E[i][0]+n*A,E[i][0]+n*(A^1));//can't be chosen
		else Add_Edge(E[i][0]+n*A,E[i][2]+n*B),Add_Edge(E[i][2]+n*(B^1),E[i][0]+n*(A^1));
	}
	for(int i = 1;i <= (n<<1);++ i) if(!dfn[i]) Tarjan(i);
	for(int i = 1;i <= n;++ i) if(bl[i] == bl[i+n]) return;
	for(int i = 1;i <= n;++ i) putchar('A'+cs[i][bl[i+n] < bl[i]]);
	exit(0);
}
void dfs(int x){
	if(x == arb.size()){
		solve();
		return;
	}
	for(char s = 'a';s < 'c';++ s) {
		Get(arb[x],s);
		dfs(x+1);
	}
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read(); Read();
	scanf("%s",c+1);
	for(int i = 1;i <= n;++ i)
		if(c[i] == 'x') arb.emplace_back(i);
		else Get(i,c[i]);
	m = Read();
	for(int i = 1;i <= m;++ i) E[i][0] = Read(),E[i][1] = gc()-'A',E[i][2] = Read(),E[i][3] = gc()-'A';
	dfs(0);
	Put(-1,'\n');
	return 0;
}
炼狱版?
//12252024832524
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std; 

typedef long long LL;
const int MAXN = 50005 << 1;
int n,m,D;
char c[MAXN];

LL Read()
{
	LL x = 0,f = 1;char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

mt19937 ee(time(0));

char gc(){
	char c = getchar();
	while(c > 'D' || c < 'A') c = getchar();
	return c;
}

vector<int> arb;
int head[MAXN],tot;
struct edge{
	int v,nxt;
}e[MAXN<<1];
void Add_Edge(int u,int v){
	e[++tot] = edge{v,head[u]};
	head[u] = tot;
}

int cs[MAXN][2],E[MAXN][4];
void Get(int i,char s){
	if(s == 'a') cs[i][0] = 1,cs[i][1] = 2;
	else if(s == 'b') cs[i][0] = 0,cs[i][1] = 2;
	else if(s == 'c') cs[i][0] = 0,cs[i][1] = 1;
}
int Find(int x,int s){
	if(cs[x][0] == s) return 0;
	else if(cs[x][1] == s) return 1;
	return 2;
}
int dfn[MAXN],dfntot,bl[MAXN],qlt,low[MAXN],st[MAXN],tl;
bool ins[MAXN];
void Tarjan(int x){
	ins[x] = 1; st[++tl] = x;
	dfn[x] = low[x] = ++dfntot;
	for(int i = head[x],v; i ;i = e[i].nxt){
		v = e[i].v;
		if(!dfn[v]) Tarjan(v),low[x] = Min(low[x],low[v]);
		else if(ins[v]) low[x] = Min(low[x],dfn[v]);
	}
	if(dfn[x] == low[x]){
		int v; ++qlt;
		do{
			bl[v = st[tl--]] = qlt;
			ins[v] = 0;
		}while(v ^ x);
	}
}
void solve(){
	for(int i = (n<<1);i >= 1;-- i) dfn[i] = head[i] = bl[i] = ins[i] = 0;
	tl = qlt = tot = dfntot = 0;
	for(int i = 1,A,B;i <= m;++ i){
		if((A = Find(E[i][0],E[i][1])) > 1) continue;
		B = Find(E[i][2],E[i][3]);
		if(B > 1) Add_Edge(E[i][0]+n*A,E[i][0]+n*(A^1));//can't be chosen
		else Add_Edge(E[i][0]+n*A,E[i][2]+n*B),Add_Edge(E[i][2]+n*(B^1),E[i][0]+n*(A^1));
	}
	for(int i = 1;i <= (n<<1);++ i) if(!dfn[i]) Tarjan(i);
	for(int i = 1;i <= n;++ i) if(bl[i] == bl[i+n]) return;
	for(int i = 1;i <= n;++ i) putchar('A'+cs[i][bl[i+n] < bl[i]]);
	exit(0);
}
char ff[10][3];
void dfs(int x){
	if(clock() > 1.98*CLOCKS_PER_SEC){
		Put(-1,'\n');
		exit(0);
	}
	if(x == D){
		solve();
		return;
	}
	for(int s = 0;s < 2;++ s) {
		Get(arb[x],ff[x][s]);
		dfs(x+1);
	}
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read(); D = Read();
	scanf("%s",c+1);
	for(int i = 1;i <= n;++ i)
		if(c[i] == 'x') arb.emplace_back(i);
		else Get(i,c[i]);
	m = Read();
	for(int i = 1;i <= m;++ i) E[i][0] = Read(),E[i][1] = gc()-'A',E[i][2] = Read(),E[i][3] = gc()-'A';
	for(int i = 0;i < D;++ i){
		ff[i][0] = 'a'; ff[i][1] = 'b'; ff[i][2] = 'c';
		shuffle(ff[i],ff[i]+3,ee);
	}
	dfs(0);
	Put(-1,'\n');
	return 0;
}

后记

炼狱版代码加个?是因为那个不是炼狱版正解(我懒得写正解了),而是O2O3+随机地图+掐表的代码,虽然过了,但不保证每次都能过。

在我写完题解的时候(2022.3.4 21:42) U群仍未停止讨论,有大佬试图找出 \(O(1.5^dn)\) 的确定性做法,如果讨论出来了,我可能会更新。

posted @ 2022-03-04 21:42  皮皮刘  阅读(46)  评论(0编辑  收藏  举报