[NOI2017] 遊戲
阿新 • • 發佈:2022-03-04
小丑竟是我自己。
前言
這道題把U群搞得很熱鬧,順便教育了一波自以為卡常的菜逼(我)。
題目
講解
普通版
我們發現雖然總共有 3 種車可以選,但是每個地圖有 1 種車不能選,所以排除掉離譜的 3-SAT 之後,還是一個比較簡單的 2-SAT 板題。
一個比較 naive 的想法是列舉 x 處選什麼車,這樣複雜度是 \(O(3^dn)\) 的,應該在哪裡都過不了,說不定能過原始資料。
但實際上我們只需要列舉 x 處選什麼地圖,根據鴿巢原理,我們發現任意兩個地圖就可以包含三種車,所以複雜度變成 \(O(2^dn)\),普通版可過。
煉獄版
WARNING:前方高能,非戰鬥人員請迅速撤離!
但是普通版的程式碼交到 UOJ 上是過不了的,我的程式碼會 T 在 extest12 上,而且我們一個機房都過不了。
於是我就去 U 群裡面問
EI 表示是卡常(此時我以為正解就是c=2)
然後事情就不對勁起來了
其中混進了一隻萌萌的兔(蛙)隊
隨後 $哥哥 給出做法
最後一個我不認識的大佬給出補充
大家都看懂了把,那我就不講了,所以複雜度就是 \(O(1.5^dn)\)。
程式碼
普通版
//12252024832524 #include <bits/stdc++.h> #define TT template<typename T> using namespace std; typedef long long LL; const int MAXN = 50005 << 1; int n,m; char c[MAXN]; LL Read() { LL x = 0,f = 1;char c = getchar(); while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();} return x * f; } TT void Put1(T x) { if(x > 9) Put1(x/10); putchar(x%10^48); } TT void Put(T x,char c = -1) { if(x < 0) putchar('-'),x = -x; Put1(x); if(c >= 0) putchar(c); } TT T Max(T x,T y){return x > y ? x : y;} TT T Min(T x,T y){return x < y ? x : y;} TT T Abs(T x){return x < 0 ? -x : x;} char gc(){ char c = getchar(); while(c > 'D' || c < 'A') c = getchar(); return c; } vector<int> arb; int head[MAXN],tot; struct edge{ int v,nxt; }e[MAXN<<1]; void Add_Edge(int u,int v){ e[++tot] = edge{v,head[u]}; head[u] = tot; } int cs[MAXN][2],E[MAXN][4]; void Get(int i,char s){ if(s == 'a') cs[i][0] = 1,cs[i][1] = 2; else if(s == 'b') cs[i][0] = 0,cs[i][1] = 2; else if(s == 'c') cs[i][0] = 0,cs[i][1] = 1; } int Find(int x,int s){ if(cs[x][0] == s) return 0; else if(cs[x][1] == s) return 1; return 2; } int dfn[MAXN],dfntot,bl[MAXN],qlt,low[MAXN],st[MAXN],tl; bool ins[MAXN]; void Tarjan(int x){ ins[x] = 1; st[++tl] = x; dfn[x] = low[x] = ++dfntot; for(int i = head[x],v; i ;i = e[i].nxt){ v = e[i].v; if(!dfn[v]) Tarjan(v),low[x] = Min(low[x],low[v]); else if(ins[v]) low[x] = Min(low[x],dfn[v]); } if(dfn[x] == low[x]){ int v; bl[x] = ++qlt; do{ bl[v = st[tl--]] = qlt; ins[v] = 0; }while(v ^ x); } } void solve(){ for(int i = (n<<1);i >= 1;-- i) dfn[i] = head[i] = bl[i] = ins[i] = 0; tl = qlt = tot = dfntot = 0; for(int i = 1,A,B;i <= m;++ i){ if((A = Find(E[i][0],E[i][1])) > 1) continue; B = Find(E[i][2],E[i][3]); if(B > 1) Add_Edge(E[i][0]+n*A,E[i][0]+n*(A^1));//can't be chosen else Add_Edge(E[i][0]+n*A,E[i][2]+n*B),Add_Edge(E[i][2]+n*(B^1),E[i][0]+n*(A^1)); } for(int i = 1;i <= (n<<1);++ i) if(!dfn[i]) Tarjan(i); for(int i = 1;i <= n;++ i) if(bl[i] == bl[i+n]) return; for(int i = 1;i <= n;++ i) putchar('A'+cs[i][bl[i+n] < bl[i]]); exit(0); } void dfs(int x){ if(x == arb.size()){ solve(); return; } for(char s = 'a';s < 'c';++ s) { Get(arb[x],s); dfs(x+1); } } int main() { // freopen(".in","r",stdin); // freopen(".out","w",stdout); n = Read(); Read(); scanf("%s",c+1); for(int i = 1;i <= n;++ i) if(c[i] == 'x') arb.emplace_back(i); else Get(i,c[i]); m = Read(); for(int i = 1;i <= m;++ i) E[i][0] = Read(),E[i][1] = gc()-'A',E[i][2] = Read(),E[i][3] = gc()-'A'; dfs(0); Put(-1,'\n'); return 0; }
煉獄版?
//12252024832524 #pragma GCC optimize(2) #pragma GCC optimize(3) #include <bits/stdc++.h> #define TT template<typename T> using namespace std; typedef long long LL; const int MAXN = 50005 << 1; int n,m,D; char c[MAXN]; LL Read() { LL x = 0,f = 1;char c = getchar(); while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();} return x * f; } TT void Put1(T x) { if(x > 9) Put1(x/10); putchar(x%10^48); } TT void Put(T x,char c = -1) { if(x < 0) putchar('-'),x = -x; Put1(x); if(c >= 0) putchar(c); } TT T Max(T x,T y){return x > y ? x : y;} TT T Min(T x,T y){return x < y ? x : y;} TT T Abs(T x){return x < 0 ? -x : x;} mt19937 ee(time(0)); char gc(){ char c = getchar(); while(c > 'D' || c < 'A') c = getchar(); return c; } vector<int> arb; int head[MAXN],tot; struct edge{ int v,nxt; }e[MAXN<<1]; void Add_Edge(int u,int v){ e[++tot] = edge{v,head[u]}; head[u] = tot; } int cs[MAXN][2],E[MAXN][4]; void Get(int i,char s){ if(s == 'a') cs[i][0] = 1,cs[i][1] = 2; else if(s == 'b') cs[i][0] = 0,cs[i][1] = 2; else if(s == 'c') cs[i][0] = 0,cs[i][1] = 1; } int Find(int x,int s){ if(cs[x][0] == s) return 0; else if(cs[x][1] == s) return 1; return 2; } int dfn[MAXN],dfntot,bl[MAXN],qlt,low[MAXN],st[MAXN],tl; bool ins[MAXN]; void Tarjan(int x){ ins[x] = 1; st[++tl] = x; dfn[x] = low[x] = ++dfntot; for(int i = head[x],v; i ;i = e[i].nxt){ v = e[i].v; if(!dfn[v]) Tarjan(v),low[x] = Min(low[x],low[v]); else if(ins[v]) low[x] = Min(low[x],dfn[v]); } if(dfn[x] == low[x]){ int v; ++qlt; do{ bl[v = st[tl--]] = qlt; ins[v] = 0; }while(v ^ x); } } void solve(){ for(int i = (n<<1);i >= 1;-- i) dfn[i] = head[i] = bl[i] = ins[i] = 0; tl = qlt = tot = dfntot = 0; for(int i = 1,A,B;i <= m;++ i){ if((A = Find(E[i][0],E[i][1])) > 1) continue; B = Find(E[i][2],E[i][3]); if(B > 1) Add_Edge(E[i][0]+n*A,E[i][0]+n*(A^1));//can't be chosen else Add_Edge(E[i][0]+n*A,E[i][2]+n*B),Add_Edge(E[i][2]+n*(B^1),E[i][0]+n*(A^1)); } for(int i = 1;i <= (n<<1);++ i) if(!dfn[i]) Tarjan(i); for(int i = 1;i <= n;++ i) if(bl[i] == bl[i+n]) return; for(int i = 1;i <= n;++ i) putchar('A'+cs[i][bl[i+n] < bl[i]]); exit(0); } char ff[10][3]; void dfs(int x){ if(clock() > 1.98*CLOCKS_PER_SEC){ Put(-1,'\n'); exit(0); } if(x == D){ solve(); return; } for(int s = 0;s < 2;++ s) { Get(arb[x],ff[x][s]); dfs(x+1); } } int main() { // freopen(".in","r",stdin); // freopen(".out","w",stdout); n = Read(); D = Read(); scanf("%s",c+1); for(int i = 1;i <= n;++ i) if(c[i] == 'x') arb.emplace_back(i); else Get(i,c[i]); m = Read(); for(int i = 1;i <= m;++ i) E[i][0] = Read(),E[i][1] = gc()-'A',E[i][2] = Read(),E[i][3] = gc()-'A'; for(int i = 0;i < D;++ i){ ff[i][0] = 'a'; ff[i][1] = 'b'; ff[i][2] = 'c'; shuffle(ff[i],ff[i]+3,ee); } dfs(0); Put(-1,'\n'); return 0; }
後記
煉獄版程式碼加個?
是因為那個不是煉獄版正解(我懶得寫正解了),而是O2O3+隨機地圖+掐表的程式碼,雖然過了,但不保證每次都能過。
在我寫完題解的時候(2022.3.4 21:42) U群仍未停止討論,有大佬試圖找出 \(O(1.5^dn)\) 的確定性做法,如果討論出來了,我可能會更新。