题目链接
题意:有A、B、C、三种车,有n局游戏,每局游戏只能用一种车,每局有一种车不能用,也有一些局三种车都能用,但是三种车都能用的不超过8局,还有一些第I局用了某种车第j局就必须用某种车的限制,求一种合法的方案(若无合法方案,则输出-1)。
题解:
首先根据部分分的小提示,我们发现了三种车都可以的地图只会出现不超过8次,而且很多测试点是没有这种地图的。那么我们就先考虑没有这种地图应该怎么做。
我们发现,这道题是每一局都要从3种车中选一种,还有一些限制条件,然后再看到好几万的数据范围,一脸懵逼——3-SAT怎么可能可做?既然我们知道,3-SAT不可做,题目又出出来了,那么我们就应该去想办法把它转化成2-SAT问题。
对2-SAT有一定了解的话,这个转化可能并不难想到。我们发现,因为每一局都有一个限制,那么相当于每局都是二选一,那么我们可以把这两种情况看作2-SAT中的两个对立节点,这两者中必选一个。
然后我们处理那些限制,由于2-SAT的连边是根据“必须”关心来连的,即我们选了
就必须选
连到的所有点,所以用这个性质来处理限制。我们设
为第
局假如选择的车,设
为第
局限制必须选的车。如果第
局本身就不能选
这种车,那么直接跳过这条限制。如果第
局有
这种车可选,但是第
局本身不能选
这种车,那么意味着如果第
局选了
,那么第
局就要选
,但是这样是不可行的,那么我们就必须不选
,而去选它的对立节点,所以这种情况就从
向它的对立节点连边。如果两种本身都在该局可选,那么我们就从
向
连边,由于2-SAT要用逆否命题保证对称性(我是这么理解的,如果不对敬请斧正),所以我们还有再从
的对立节点向
的对立节点连边即可。这样图就建好了,用2-SAT模板就能求出答案了。
2-SAT输出方案是缩点、判完有解之后建缩后的点的反图,进行拓扑排序,如果点
所在的连通块的拓扑序在
之后,那么用第一个点,否则用第二个点。但是由于tatrjan的求强连通分量的过程中连通块的编号就是按拓扑排序逆序给出的(不是非常明白),所以只需要比较
与
所在的连通块的编号即可完成判断(可以理解为卡了一波常数?)。
那么这样,对于没有三种车都可以选的局的情况我们就做完了。接下来就该考虑如何处理三种车可以用的局。
由于我们发现这样的局很少,所以我们考虑暴力处理,由刚才的做法我们得到启发,假如一个局有了一种车是不能用的限制,我们就会做了,那么我们就枚举每局以及依次限制A车、B车、C车,每次建图跑2-SAT,这样我们就得到了一个
的做法,(似乎是能过90分),但是还是过不了!其实我们要做的是让这些没有限制的局也从A、B、C中选一个,从而形成合法解,那么其实对于每一个无限制的局,前两次的枚举限制无论是限制不能选哪两种车,都已经提供了3种可选的车。具体点解释就是,假如前两次我们分别现在不能选A车、不能选B车,那么第一次B、C车可以选,第二次A、C车可以选,三种车已经都有了被选择的机会,所以不需要再去枚举第三次了。于是我们就得到了一个
的做法,就可以过掉本题了。
思路想出来之后代码里还是有些细节的,写错细节而丢分还是很难受的。这个题我的代码写得很不简练。
PS:感叹两句,好像好久没写博客了,暑假过得真快啊。暑假里果然是颓废,写的题看了看也没什么值得记的。最近学了一下2-SAT,但是感觉理解得并不深刻,所以没有写学习笔记。一些比较简单的2-SAT没有什么值得记的地方,所以就很久没有写博客了。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,d,m,hed[100010],cnt,book[10],ji,u[300010],v[300010],ct;
int dfn[100010],low[100010],vis[100010],sta[100010],tp,kuai[100010],pd,shu,tot,pan;
char s[50010],cur[500010][2];
struct node
{
int to,next;
}a[1000100];
struct limit
{
int x,y;
char hx,hy;
}b[100010];
inline void add(int from,int to)
{
a[++cnt].to=to;
a[cnt].next=hed[from];
hed[from]=cnt;
}
inline void tarjan(int x)
{
sta[++tp]=x;
vis[x]=1;
dfn[x]=low[x]=++shu;
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(vis[y])
low[x]=min(low[x],low[y]);
}
if(dfn[x]==low[x])
{
++tot;
while(sta[tp+1]!=x)
{
kuai[sta[tp]]=tot;
vis[sta[tp]]=0;
--tp;
}
}
}
int main()
{
scanf("%d%d",&n,&d);
scanf("%s",s+1);
scanf("%d",&m);
for(int i=1;i<=m;++i)
cin>>b[i].x>>b[i].hx>>b[i].y>>b[i].hy;
for(int i=1;i<=n;++i)
{
if(s[i]=='x')
book[++ji]=i;
}
for(int i=0;i<=(1<<d)-1;++i)
{
for(int j=0;j<=d-1;++j)
{
if((1<<j)&i)
s[book[j+1]]='a';
else
s[book[j+1]]='b';
}
for(int j=1;j<=cnt;++j)
{
a[i].to=0;
a[i].next=0;
}
memset(hed,0,sizeof(hed));
cnt=0;
ct=0;
for(int i=1;i<=n;++i)
{
if(s[i]=='a')
{
cur[i][0]='B';
cur[i][1]='C';
}
else if(s[i]=='b')
{
cur[i][0]='A';
cur[i][1]='C';
}
else
{
cur[i][0]='A';
cur[i][1]='B';
}
}
for(int j=1;j<=m;++j)
{
if(s[b[j].x]+1-'a'==b[j].hx+1-'A')
continue;
if(s[b[j].y]+1-'a'==b[j].hy+1-'A')
{
if(b[j].hx==cur[b[j].x][0])
{
add(b[j].x,b[j].x+n);
u[++ct]=b[j].x;
v[ct]=b[j].x+n;
}
else
{
add(b[j].x+n,b[j].x);
u[++ct]=b[j].x+n;
v[ct]=b[j].x;
}
}
else
{
if(b[j].hx==cur[b[j].x][0]&&b[j].hy==cur[b[j].y][0])
{
add(b[j].x,b[j].y);
u[++ct]=b[j].x;
v[ct]=b[j].y;
add(b[j].y+n,b[j].x+n);//Äæ·ñÃüÌâ¶Ô³Æ
u[++ct]=b[j].y+n;
v[ct]=b[j].x+n;
}
else if(b[j].hx==cur[b[j].x][0]&&b[j].hy==cur[b[j].y][1])
{
add(b[j].x,b[j].y+n);
u[++ct]=b[j].x;
v[ct]=b[j].y+n;
add(b[j].y,b[j].x+n);//Äæ·ñÃüÌâ¶Ô³Æ
u[++ct]=b[j].y;
v[ct]=b[j].x+n;
}
else if(b[j].hx==cur[b[j].x][1]&&b[j].hy==cur[b[j].y][0])
{
add(b[j].x+n,b[j].y);
u[++ct]=b[j].x+n;
v[ct]=b[j].y;
add(b[j].y+n,b[j].x);//Äæ·ñÃüÌâ¶Ô³Æ
u[++ct]=b[j].y+n;
v[ct]=b[j].x;
}
else if(b[j].hx==cur[b[j].x][1]&&b[j].hy==cur[b[j].y][1])
{
add(b[j].x+n,b[j].y+n);
u[++ct]=b[j].x+n;
v[ct]=b[j].y+n;
add(b[j].y,b[j].x);//Äæ·ñÃüÌâ¶Ô³Æ
u[++ct]=b[j].y;
v[ct]=b[j].x;
}
}
}
shu=0;
tot=0;
memset(kuai,0,sizeof(kuai));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
tp=0;
for(int j=1;j<=2*n;++j)
{
if(!dfn[j])
tarjan(j);
}
pan=0;
for(int j=1;j<=n;++j)
{
if(kuai[j]==kuai[j+n])
{
pan=1;
break;
}
}
if(pan==1)
continue;
pd=1;
for(int j=1;j<=n;++j)
{
if(kuai[j]<kuai[j+n])
{
if(s[j]=='a')
printf("B");
else
printf("A");
}
else
{
if(s[j]=='c')
printf("B");
else
printf("C");
}
}
if(pd==1)
{
printf("\n");
break;
}
}
if(pd==0)
printf("-1\n");
return 0;
}