[NOI2017]游戏
题目大意:
有n(n<=50000)场赛车游戏,每场游戏有不同的赛车,总共有ABC三种不同的场地。
赛车对使用场地有要求,a型赛车不能在A上开,b型赛车不能在B上开,c型赛车不能在C上开,x型赛车可以在任何赛场上开。
x型赛车恰好有d(d<=8)种。
特别地,还有m(m<=100000)条特殊规定,如果第i场游戏的场地为hi,那么第j场游戏一定要在hj号场地上。
问是否存在完成游戏的方案,并求出任意一种方案。
思路:
事实上两种不同的赛车就可以涵盖所有类型的场地,因此我们只需要2^d枚举一下每一个x型赛车属于a型还是b型。
对于每一场游戏,由于已经限制某种场地不能选,因此每一场只有2种情况。
这样就转化成了一个2-SAT问题。
对于m条限制,我们可以分类讨论判断连边方式。
1.若s[i]=hi,第i张地图本来就不适合u车,说明u车一定无解,不连任何边。
2.若s[j]=hj,第i张地图适合u车,但第j张地图不适合v车,就用u->u’
3.若s[i]≠hi且s[j]≠hj,两张地图都适合,那么连边u->v,再连边v′->u′,表示第j张地图如果选用v′那么第i张地图不能用u,即用u′。
然后Tarjan判断一下是否会有矛盾出现即可。
最后构造方案时,由于是任意一种方案,因此我们可以对于同一场游戏,选SCC编号小的。可以证明这样一定是正确的(然而我并不会证)。
UOJ上有一组Ext数据,所以一直被卡成97分,不过不能用正常方法过掉这组数据,
只能舍弃一些状态,比如卡时间或者随机枚举100种状态(并不能保证正确性),所以最后还是交洛谷上了。
1 #include<stack> 2 #include<cstdio> 3 #include<cctype> 4 #include<vector> 5 inline int getint() { 6 register char ch; 7 while(!isdigit(ch=getchar())); 8 register int x=ch^'0'; 9 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 10 return x; 11 } 12 inline char gettype() { 13 register char ch; 14 while(!isupper(ch=getchar())); 15 return tolower(ch); 16 } 17 const int N=50000,M=100000,D=8; 18 char s[N]; 19 bool ins[N<<1]; 20 std::stack<int> stack; 21 std::vector<int> e[N<<1]; 22 int n,d,m,pos[N],dfn[N<<1],low[N<<1],scc[N<<1],cnt,id; 23 struct Limit { 24 int i; 25 char hi; 26 int j; 27 char hj; 28 }; 29 Limit lim[M]; 30 inline void add_edge(const int &u,const int &v) { 31 e[u].push_back(v); 32 } 33 inline void reset() { 34 while(!stack.empty()) stack.pop(); 35 cnt=id=0; 36 for(register int i=0;i<n<<1;i++) { 37 e[i].clear(); 38 dfn[i]=ins[i]=0; 39 } 40 } 41 inline void tarjan(const int &x) { 42 stack.push(x); 43 ins[x]=true; 44 dfn[x]=low[x]=++cnt; 45 for(unsigned i=0;i<e[x].size();i++) { 46 const int &y=e[x][i]; 47 if(!dfn[y]) { 48 tarjan(y); 49 low[x]=std::min(low[x],low[y]); 50 } else if(ins[y]) { 51 low[x]=std::min(low[x],dfn[y]); 52 } 53 } 54 if(low[x]==dfn[x]) { 55 id++; 56 int y=-1; 57 while(y!=x) { 58 y=stack.top(); 59 stack.pop(); 60 ins[y]=false; 61 scc[y]=id; 62 } 63 } 64 } 65 inline int getpos(const int &x,const char &y) { 66 if(s[x]=='a') { 67 if(y=='b') return x<<1; 68 if(y=='c') return x<<1|1; 69 } 70 if(s[x]=='b') { 71 if(y=='a') return x<<1; 72 if(y=='c') return x<<1|1; 73 } 74 if(s[x]=='c') { 75 if(y=='a') return x<<1; 76 if(y=='b') return x<<1|1; 77 } 78 return 0; 79 } 80 inline char getval(const int &x,const bool &y) { 81 if(s[x]=='a') { 82 if(y==false) return 'B'; 83 if(y==true) return 'C'; 84 } 85 if(s[x]=='b') { 86 if(y==false) return 'A'; 87 if(y==true) return 'C'; 88 } 89 if(s[x]=='c') { 90 if(y==false) return 'A'; 91 if(y==true) return 'B'; 92 } 93 return 0; 94 } 95 inline bool solve() { 96 for(register int i=0;i<m;i++) { 97 if(s[lim[i].i]==lim[i].hi) continue; 98 const int u=getpos(lim[i].i,lim[i].hi),v=getpos(lim[i].j,lim[i].hj); 99 if(s[lim[i].j]==lim[i].hj) add_edge(u,u^1); 100 if(s[lim[i].i]!=lim[i].hi&&s[lim[i].j]!=lim[i].hj) { 101 add_edge(u,v); 102 add_edge(v^1,u^1); 103 } 104 } 105 for(register int i=0;i<n<<1;i++) { 106 if(!dfn[i]) tarjan(i); 107 } 108 for(register int i=0;i<n;i++) { 109 if(scc[i<<1]==scc[i<<1|1]) return false; 110 } 111 for(register int i=0;i<n;i++) { 112 putchar(getval(i,scc[i<<1]>=scc[i<<1|1])); 113 } 114 return true; 115 } 116 int main() { 117 n=getint(),d=getint(); 118 scanf("%s",s); 119 for(register int i=0,cnt=0;i<n;i++) { 120 if(s[i]=='x') pos[cnt++]=i; 121 } 122 m=getint(); 123 for(register int i=0;i<m;i++) { 124 lim[i]=(Limit){getint()-1,gettype(),getint()-1,gettype()}; 125 } 126 for(register int k=0;k<1<<d;k++) { 127 for(register int i=0;i<d;i++) { 128 s[pos[i]]='a'+(k>>i&1); 129 } 130 if(solve()) return 0; 131 reset(); 132 } 133 puts("-1"); 134 return 0; 135 }