题目链接:https://www.luogu.com.cn/problem/P3825
我太蠢了呀。
题意读错想半天。蠢的跟猪一样。
最后看了题解才晓得题意读错了,猪,你为何这么蠢。
先来个题意知识:
我们考虑一下当d为0的时候:
那么,每个地图最多会有两辆车子。而且每个地图一定是会从中选取一辆车。
那这也就是一个2-SAT问题的模板了。只是说我们要仔细考虑。
如果xi地图为B,则i表示选择A车,i+n表示选择C车
如果地图为C,则i表示选择A车,i+n表示选择B车
如果地图为A,则i表示选择B车,i+n表示选择C车。
考虑建边问题:
对于一个四元组(i,hi,j,hj)
如果str[i]==hi,则表示地图i中,根本不可能选择hi这辆车,那这个直接continue就好。
在上面条件不满足情况下,如果str[j]==hj,则表示,j这个位置一定不可以选择hj这辆车。那么我们可以把i和i+n进行连接,表示如果i这个位置选择了hi,则也一定要选择i’。
在上面条件都不满足情况下,也就是说i这个为位置可以选择hi,j这个位置可以选择hj。
那我肯定要i指向j的位置。同时因为逆否命题,我要j'指向i'这个位置。
然后观察得到d值较小。我们可以直接枚举每个X位置会为什么情况。
理论上,我们需要枚举每个x位置分别为A,B,C 的情况
也就是:
1,x位置为A,则可选车为B和C
2,x位置为B,则可选车为A和C
3,x位置为C,则可选车为A和B
我们可以发现,第一种和第二种其实已经包含了第三种。
所以我们只要枚举两种情况即可。
#include"stdio.h"
#include"string.h"
#include"vector"
#include"algorithm"
using namespace std;
#define OK printf("
");
#define exit return 0;
const int N = 1e6 * 2 + 1010;
const int M = 2 * N;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
int head[N],ver[M],Next[M],tot;int n,m,num,top,cnt;
int dfn[N],low[N],id[110];int limit = 0;
int Stack[N],ins[N],c[N];
int s[N]; char str[N];
int X[N],Y[N]; char cx[N],cy[N];
void add(int x,int y){
ver[++ tot] = y; Next[tot] = head[x];
head[x] = tot;
}
void tarjan(int x){
dfn[x] = low[x] = ++ num;
Stack[++ top] = x; ins[x] = 1;
for(int i = head[x]; i; i = Next[i])
if(!dfn[ver[i]]) {
tarjan(ver[i]);
low[x] = min(low[x],low[ver[i]]);
} else if(ins[ver[i]])
low[x] = min(low[x],dfn[ver[i]]);
if(dfn[x] == low[x]){
cnt ++; int y;
do{
y = Stack[top --];ins[y] = 0;
c[y] = cnt;
} while(x != y);
}
}
void Build_edge()
{
for(int i = 1; i <= m; i ++){
if(str[X[i]] == cx[i]) continue;
if(str[Y[i]] == cy[i]) {
if(cx[i] == 'C' || (cx[i] == 'B' && str[X[i]] == 'C')) {
add(X[i] + n,X[i]); //printf("%d -> %d
",X[i] + n,X[i]);
} else {
add(X[i],X[i] + n); //printf("%d -> %d
",X[i],X[i] + n);
}
continue;
}
int v1 = (cy[i] == 'C' || (cy[i] == 'B' && str[Y[i]] == 'C')) ? 1 : 0;
int v2 = (cx[i] == 'C' || (cx[i] == 'B' && str[X[i]] == 'C')) ? 1 : 0;
add(X[i] + n * (v2),Y[i] + n * v1);
add(Y[i] + n * (v1 ^ 1),X[i] + n * (v2 ^ 1));
// printf("%d -> %d
",X[i] + n *(v2),Y[i] + n * v1);
// printf("%d -> %d
",Y[i] + n * (v1 ^ 1),X[i] + n * (v2 ^ 1));
}
return ;
}
void init()
{
num = 0; memset(ins,0,sizeof(ins));
cnt = 0; tot = 0; top = 0;
memset(low,0,sizeof(low)); memset(c,0,sizeof(c));
memset(dfn,0,sizeof(dfn));memset(Stack,0,sizeof(Stack));
memset(head,0,sizeof(head));
}
void solve(){
int mark = 1;
for(int i = 0; i < (1 << limit); i ++){
init();mark = 1;
for(int j = 1; j <= limit; j ++){
str[id[j]] = (i & (1 << (j - 1))) ? 'A' : 'B';
}
Build_edge();
for(int i = 1; i <= n * 2; i ++){
if(!dfn[i]) tarjan(i);
}
for(int i = 1; i <= n; i ++)
if(c[i] == c[i + n]) {mark = 0; break;}
if(mark == 0) continue;
for(int i = 1; i <= n; i ++){
if(c[i] < c[i + n]){
if(str[i] == 'A') printf("B");
else printf("A");
} else {
if(str[i] == 'C') printf("B");
else printf("C");
}
}
return ;
}
printf("-1"); return ;
}
int main()
{
int d;
scanf("%d%d",&n,&d);
scanf("%s",str + 1);
for(int i = 1; i <= n; i ++)
{
if(str[i] == 'x') id[++ limit] = i;
else str[i] -= 32;
}
m = read();
for(int i = 1; i <= m; i ++){
scanf("%d %c %d %c",&X[i],&cx[i],&Y[i],&cy[i]);
}
solve();
return 0;
}
小 L 对游戏有一些特殊的要求,这些要求可以用四元组 (i,hi,j,hj)来描述,表示若在第i场使用型号为hi的车子,则第j场游戏要使用型号为hj的车子。
这句话的意思应该是在第i场选择了hi的时候,则第j场必须选择hj。
但是是建了反向边的呢?也就是说: 若两个都可用,则从i向j连边,表示若选i,则一定选j,同时从j′向i′连边,这里表示若没有选j,则一定没有选i。
为啥没有选hj,则i那个位置不能选hi呢?题目只是说i选择了hi,则j必须选择hj呀。我相信有部分同学会有这个困惑。
解释: