Luogu P3825 [NOI2017]遊戲
阿新 • • 發佈:2018-11-10
這道題看上去NPC啊,超級不可做的樣子。
我們先分析一下簡單的情形:沒有\(x\)地圖
此時每個地圖由於限制掉一種汽車,那麼顯然只會有兩種選擇。
再考慮到限制的情況,那麼大致做法就很顯然了——2-SAT
首先是拆點,對於每張地圖\(i\)拆成\(2i-1\)與\(2i\)表示這張地圖選擇能用的車的第一輛還是第二輛。
比如如果\(s_i=b\),那麼\(2i-1\)表示選擇\(A\)車,\(2i\)表示選擇\(B\)車。
現在開始考慮選擇的限制,對於每一個限制\((u,x,v,y)\),我們分情況討論:
- 當\(s_u=x\),說明這個限制沒有意義,直接無視即可。
- 當\(s_u\not=x\)
- 當\(s_u\not=x\)且\(s_v\not= y\)時,顯然講\(x\)對應的點向\(y\)對應的點連邊。但是不要忘了2-SAT建圖的對稱性,我們還要從\(y\)對應的點的反點向\(x\)對應的點的反點連邊。這樣的意義也十分明確了吧,因為每個地圖都必須做出選擇,選擇\(y\)對應的點的反點就使得\(u\)沒有其他選擇了。
然後就是用Tarjan跑SCC的過程了,這裡不再贅述。值得一提的是Tarjan的標號順序是按照拓撲序的逆序(因為是DFS)跑出來的,所以可以直接用標號來輸出方案。
那麼接下來就是考慮萬惡的\(x\)地圖了
我們發現這樣並沒有什麼好的建圖方法,然後翻到下面一看資料範圍\(d\le8\)
然後一種特別naive的想法就是\(3^d\)列舉每個\(x\)地圖是什麼型別。然後再乘上Tarjan的複雜度直接爆炸。
我們想一下我們列舉的本質:
- 如果對於一個\(x\)地圖取\(a\)即意味這隻能用車\(B,C\)
- 如果對於一個\(x\)地圖取\(b\)即意味這隻能用車\(A,C\)
- 如果對於一個\(x\)地圖取\(c\)即意味這隻能用車\(A,B\)
然後我們驚奇地發現第三種情況已經被前兩種情況包含掉了,所以複雜度變為\(2^d(n+m)\)
如果對於爆搜會被無解的資料卡滿的同志們可以直接隨機跑,實測這樣也可以過。
不過實踐的時候細節巨多,還是挺煩人的。
CODE
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#define RI register int
#define Ms(f,x) memset(f,x,sizeof(f))
using namespace std;
const int N=50005;
struct ques
{
int x,y; char p1,p2;
}q[N<<1]; int n,m,d,tot,c[N],ukn[10],pt; char ch;
inline int T(char ch)
{
if (ch>='a'&&ch<='c') return ch-'a'+1;
else return ch-'A'+1;
}
inline int Id(int x,int y)
{
switch (x)
{
case 1:return y!=3?1:0;break;
case 2:return y!=3?1:0;break;
case 3:return y!=2?1:0;break;
}
}
inline char C(int x,int y)
{
switch (x)
{
case 1:return y!=2?'B':'C';break;
case 2:return y!=2?'A':'C';break;
case 3:return y!=2?'A':'B';break;
}
}
class FileInputOutput
{
private:
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define S 1<<21
char Fin[S],Fout[S],*A,*B; int Ftop;
public:
FileInputOutput() { Ftop=0; A=B=Fin; }
inline void gc(char &ch)
{
while (!isalpha(ch=tc()));
}
inline void pc(char ch)
{
Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch);
}
inline void read(int &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
inline void Fend(void)
{
fwrite(Fout,1,Ftop,stdout);
}
#undef S
#undef tc
}F;
class Two_SAT_Solver
{
private:
#define add(x,y) e[++cnt]=(edge){y,head[x]},head[x]=cnt
struct edge
{
int to,next;
}e[N<<2]; int head[N<<1],cnt,dfn[N<<1],low[N<<1],scc,col[N<<1],stack[N<<1],top,tot; bool vis[N];
inline int min(int a,int b)
{
return a<b?a:b;
}
#define to e[i].to
inline void Tarjan(int now)
{
dfn[now]=low[now]=++tot; stack[++top]=now; vis[now]=1;
for (RI i=head[now];i;i=e[i].next)
if (!dfn[to]) Tarjan(to),low[now]=min(low[now],low[to]);
else if (vis[to]) low[now]=min(low[now],dfn[to]);
if (low[now]==dfn[now])
{
col[now]=++scc; vis[now]=0; while (stack[top]!=now)
col[stack[top]]=scc,vis[stack[top--]]=0; --top;
}
}
#undef to
public:
inline void build(void)
{
for (RI i=1;i<=m;++i)
{
int x=q[i].x,y=q[i].y,p1=T(q[i].p1),p2=T(q[i].p2);
if (c[x]==p1) continue; int numx=x<<1,numy=y<<1;
if (c[y]==p2) add(numx-Id(c[x],p1),numx-(Id(c[x],p1)^1)); else
add(numx-Id(c[x],p1),numy-Id(c[y],p2)),add(numy-(Id(c[y],p2)^1),numx-(Id(c[x],p1)^1));
}
}
inline void check(void)
{
RI i; int lim=n<<1; for (i=1;i<=lim;++i) if (!dfn[i]) Tarjan(i);
for (i=1;i<=n;++i) if (col[(i<<1)-1]==col[i<<1]) return;
for (i=1;i<=n;++i) if (col[(i<<1)-1]<col[i<<1]) F.pc(C(c[i],1)); else F.pc(C(c[i],2));
F.Fend(); exit(0);
}
inline void clear(void)
{
cnt=tot=scc=0; Ms(head,0); Ms(dfn,0);
}
}S;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j; for (F.read(n),F.read(d),i=1;i<=n;++i)
{
F.gc(ch); if (ch!='x') c[i]=T(ch); else ukn[++pt]=i;
}
for (F.read(m),i=1;i<=m;++i) F.read(q[i].x),F.gc(q[i].p1),F.read(q[i].y),F.gc(q[i].p2);
for (tot=(1<<d)-1,i=0;i<=tot;++i)
{
for (j=1;j<=d;++j) c[ukn[j]]=((i>>j-1)&1)+1;
S.build(); S.check(); S.clear();
}
return F.pc('-'),F.pc('1'),F.Fend(),0;
}