[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 }
原文地址:https://www.cnblogs.com/skylee03/p/8185647.html