題意同3495,但是記憶體限制收緊了,不能採用3495的前字尾優化建圖的方式。
注意到“每個集合恰好選擇一個點”可以放寬成“每個集合最多選擇一個點”,對於最後求出的方案裡,如果某個集合沒選點,任選一個就好了。
考慮2-SAT建圖,有兩類邊:
1. 對於每條給定的邊$(u,v)$:如果不選$u$就必須選$v$,如果不選$v$就必須選$u$。
2. 對於每個集合:如果選了一個點就不能選其它所有點。
第二類邊不能直接建圖,但是在Kosaraju演算法中DFS圖的時候,每個點$x$和$x$所在集合內除了$x$之外的所有點都連了一條第二類邊,需要用一個資料結構跳過那些已經搜過的且不是$x$的點。用一個支援雙端pop的佇列維護就可以了,如果這個集合不是隻剩$x$沒搜過,那麼兩端至少可以消費一個點。
時間複雜度$O(n+m+k)$。
#include<cstdio> const int N=2000010,M=1000010,BUF=25000000; char Buf[BUF],*buf=Buf; int n,m,K,o,i,j,x,y,S[M],T[M],st[M],en[M],pool[M],tot,at[M]; int e[M][2],g[N],v[N],nxt[N],ed; int q[N],t,f[N]; bool vis[N]; inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;} inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} void dfs1(int x){ if(vis[x])return; vis[x]=1; for(int i=g[x];i;i=nxt[i])dfs1(v[i]); if(x>n)while(S[at[x-n]]<=T[at[x-n]]){ if(pool[S[at[x-n]]]!=x-n)dfs1(pool[S[at[x-n]]++]); else if(pool[T[at[x-n]]]!=x-n)dfs1(pool[T[at[x-n]]--]); else break; } q[++t]=x; } void dfs2(int x){ if(!vis[x])return; vis[x]=0,f[x]=o; for(int i=g[x];i;i=nxt[i])dfs2(v[i]); if(x<=n)while(S[at[x]]<=T[at[x]]){ if(pool[S[at[x]]]!=x)dfs2(pool[S[at[x]]++]+n); else if(pool[T[at[x]]]!=x)dfs2(pool[T[at[x]]--]+n); else break; } } int main(){ fread(Buf,1,BUF,stdin);read(n),read(m),read(K); for(i=1;i<=m;i++){ read(x),read(y); e[i][0]=x,e[i][1]=y; add(x,y+n),add(y,x+n); } for(i=1;i<=K;i++){ read(y); st[i]=tot+1; while(y--){ read(x); at[x]=i; pool[++tot]=x; } en[i]=tot; } for(i=1;i<=K;i++)S[i]=st[i],T[i]=en[i]; for(i=1;i<=n+n;i++)if(!vis[i])dfs1(i); for(ed=0,i=1;i<=n+n;i++)g[i]=0; for(i=1;i<=m;i++){ x=e[i][0],y=e[i][1]; add(y+n,x),add(x+n,y); } for(i=1;i<=K;i++)S[i]=st[i],T[i]=en[i]; for(i=t;i;i--)if(vis[q[i]])o++,dfs2(q[i]); for(i=1;i<=n;i++)if(f[i]==f[i+n])return puts("NIE"),0; puts("TAK"); for(i=1;i<=K;i++)st[i]=pool[st[i]]; for(i=1;i<=n;i++)if(f[i]<f[i+n])st[at[i]]=i; for(i=1;i<=K;i++)printf("%d ",st[i]); return 0; }