今天模拟很有趣,你只要写一份AC代码就能顺便用它来与好友进行k子棋和围棋的混合棋(只是不支持悔棋)。
T1.FIR
期望得分100,实际得分64,(如果有subtask是0分)
这就是那道有趣的题。大模拟……
判断赢我的办法是dfs,分别向八个方向搜索,把相对应的两个方向的答案加上看是否有一个符合。
判断死的话,从一个子开始bfs,遇到同颜色子压入队列,如果周围有一口气就能活,如果搜到最后也没有的话就是死了。
判断吃子的话,找这个子四周的子,遇到一个不同颜色的就开始判死,如果死了的话,我们把棋盘那个位置还原。(我的办法是开一个栈记录一下搜到了哪些子)
判断的顺序是:先不合法(那个位置有棋),再判吃子,再判胜利,如果吃不了子判死亡,没死判胜利。
然后本人因为打了vis标记然后……没用,凉凉。而且还忘了一次能吃好几个子,又凉凉。注意棋盘是无限的,所以其实在边上的子是有气的,而且你还杀不死它。
改完之后就好了,同时也看一下std,非常简洁……只用了我一半的码长……
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #include<utility> #include<map> #define pr pair<int,int> #define mp make_pair #define fi first #define sc second #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') using namespace std; typedef long long ll; const int M = 100005; const int N = 10000005; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >='0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct node { int x,y; }; int n,p,g[1005][1005],stack[1005][3],top,x,y; int dx[10] = {0,1,1,0,-1,-1,-1,0,1},dy[10] = {0,0,1,1,1,0,-1,-1,-1}; int sx[6] = {0,1,0,-1,0},sy[6] = {0,0,1,0,-1}; bool vis[1005][1005]; queue <node> q; bool pddie(int x,int y,bool f) { bool flag = 0; if(x == 3 && y == 4) flag = 1; while(!q.empty()) q.pop(); q.push((node){x,y}); while(!q.empty()) { node k = q.front(); q.pop(); // if(flag) printf("%d %d ",k.x,k.y); vis[k.x][k.y] = 1,stack[++top][0] = k.x,stack[top][1] = k.y; rep(i,1,4) { int kx = k.x + sx[i],ky = k.y + sy[i]; if(vis[kx][ky]) continue; //if(kx < 1 || ky < 1) continue; if(g[kx][ky] == -1) { while(top) vis[stack[top][0]][stack[top][1]] = 0,top--; return 0; } else if(g[kx][ky] == f) q.push((node){kx,ky}); } } return 1; } bool pduse(int x,int y) { return g[x][y] != -1; } bool pdeat(int x,int y,bool f) { bool fla = 0; g[x][y] = f; rep(i,1,4) { int kx = x + sx[i],ky = y + sy[i]; if(kx < 1 || ky < 1) continue; if(g[kx][ky] == (f^1)) { // printf("!%d %d ",kx,ky); if(pddie(kx,ky,f^1)) { // printf("@ "); while(top) { vis[stack[top][0]][stack[top][1]] = 0; g[stack[top][0]][stack[top][1]] = -1,top--; } fla = 1; } } } g[x][y] = -1; return fla; } int dfs(int x,int y,int dir,bool f) { if(g[x][y] != f) return 0; else if(x < 1 || y < 1) return 0; else return dfs(x + dx[dir],y + dy[dir],dir,f) + 1; } bool pdwin(int x,int y,bool f) { if(dfs(x,y,1,f) + dfs(x,y,5,f) - 1 >= p) return 1; if(dfs(x,y,2,f) + dfs(x,y,6,f) - 1 >= p) return 1; if(dfs(x,y,3,f) + dfs(x,y,7,f) - 1 >= p) return 1; if(dfs(x,y,4,f) + dfs(x,y,8,f) - 1 >= p) return 1; return 0; } void printgraph() { rep(l,1,10) { rep(j,1,10) { if(g[l][j] == 1) putchar('O'); else if(g[l][j] == 0) putchar('X'); else putchar('.'); } enter; } } int main() { freopen("fir.in","r",stdin); freopen("fir.out","w",stdout); memset(g,-1,sizeof(g)); n = read(),p = read(); rep(i,1,n) { x = read(),y = read(); if(pduse(x,y)) printf("illegal "),exit(0); else if(pdeat(x,y,i&1)) { g[x][y] = i&1; if(pdwin(x,y,i&1)) { (i&1) ? printf("ITer %d ",i) : printf("Kitty %d ",i); return 0; } } else if(pddie(x,y,i&1)) printf("illegal "),exit(0); g[x][y] = i&1; if(pdwin(x,y,i&1)) { (i&1) ? printf("ITer %d ",i) : printf("Kitty %d ",i); return 0; } // printgraph(); } printf("draw "); return 0; }
std:
#include<bits/stdc++.h> using namespace std; int f[1111][1111],n,x,y,curx,cury,X,cnt; bool used[1111][1111],fl,cur; int dx[]= {1,0,-1,0},dy[]= {0,1,0,-1}; int dxx[]= {1,-1,0,0,1,-1,1,-1},dyy[]= {0,0,1,-1,-1,1,1,-1}; vector<pair<int,int> > v; void dfs(int x,int y,int ff) { used[x][y]=1; v.push_back(make_pair(x,y)); for (int j=0; j<4; j++) { if (fl) return; int xx=x+dx[j],yy=y+dy[j]; if (used[xx][yy]) continue; if (!f[xx][yy]) { fl=1; return; } if (f[xx][yy]==ff) dfs(xx,yy,ff); } } bool DFS(int x,int y) { v.clear(); fl=0; dfs(x,y,f[x][y]); if (!fl) { for (int i=0; i<v.size(); i++) { f[v[i].first][v[i].second]=0; } } for (int i=0; i<v.size(); i++) { used[v[i].first][v[i].second]=0; } return fl; } int main() { freopen("FIR.in","r",stdin); freopen("FIR.out","w",stdout); scanf("%d%d",&n,&X); for (int i=1; i<=n; i++) { scanf("%d%d",&x,&y); if (f[x][y]) { printf("illegal "); return 0; } f[x][y]=(i&1)+1; if (f[x][y+1] && f[x][y+1]!=f[x][y]) DFS(x,y+1); if (f[x+1][y] && f[x+1][y]!=f[x][y]) DFS(x+1,y); if (f[x][y-1] && f[x][y-1]!=f[x][y]) DFS(x,y-1); if (f[x-1][y] && f[x-1][y]!=f[x][y]) DFS(x-1,y); if (!DFS(x,y)) { printf("illegal "); return 0; } for (int j=0; j<4; j++) { curx=x; cury=y; cnt=1; for (int k=0; k<X; k++) { curx+=dxx[j*2]; cury+=dyy[j*2]; if (f[curx][cury]!=f[x][y]) break; cnt++; } curx=x; cury=y; for (int k=0; k<X; k++) { curx+=dxx[j*2+1]; cury+=dyy[j*2+1]; if (f[curx][cury]!=f[x][y]) break; cnt++; } if (cnt>=X) { if (i&1) printf("ITer %d ",i); else printf("Kitty %d ",i); return 0; } } } printf("draw "); return 0; }
T2.maze
期望得分30,实际得分30
这道题考试的时候先写了30分暴力bfs,之后想到50分可以DP,但是死活没调出来……
然后考完发现自己忘记从0枚举,然后还忘记判断不能走的格子的情况了……
正解很简洁,我们必然要经过大对角线上的一点,从(1,1)走到大对角线上的一点最多有220种情况,同理(n,m)也一样。所以我们可以先处理(1,1)到大对角线上点的所以情况,之后从(n,m)出发汇合。至于如何存储数值,我们只需要用强无敌的map映射即可。
看一下代码。
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #include<utility> #include<map> #define pr pair<int,int> #define mp make_pair #define fi first #define sc second #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') using namespace std; typedef long long ll; const int M = 100005; const int N = 10000005; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >='0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int n,m,kx,a[25][25],cnt,maxn,dx[3] = {0,0,1},dy[3] = {0,1,0},dp[25][25][40005]; ll ans; map <int,ll> pm[25]; void dfs(int x,int y,int sum) { if(!a[x][y]) return; if(x + y == n + 1) { pm[x][sum]++; return; } if(x < n) dfs(x+1,y,sum^a[x+1][y]); if(y < m) dfs(x,y+1,sum^a[x][y+1]); } void getans(int x,int y,int sum) { if(!a[x][y]) return; if(x + y == n + 1) { ans += pm[x][kx^sum^a[x][y]]; return; } if(x > 1) getans(x-1,y,sum^a[x-1][y]); if(y > 1) getans(x,y-1,sum^a[x][y-1]); } int main() { n = read(),m = read(),kx = read(); rep(i,1,n) rep(j,1,m) a[i][j] = read(),maxn = max(maxn,a[i][j]); if(maxn <= 10000) { dp[1][1][a[1][1]] = 1; rep(i,1,n) { rep(j,1,m) { if(a[i+1][j]) rep(k,0,40000) dp[i+1][j][k^a[i+1][j]] += dp[i][j][k]; if(a[i][j+1]) rep(k,0,40000) dp[i][j+1][k^a[i][j+1]] += dp[i][j][k]; } } printf("%d ",dp[n][m][kx]); } else dfs(1,1,a[1][1]),getans(n,m,a[n][m]),printf("%lld ",ans); return 0; }
T3.snowman
期望得分30,实际得分30.
这个题我没有什么思路,但是要求最小值最大想到二分答案,之后好像只会暴力判断了。这个复杂度其实我不会求……因为每次判断的时间非常不稳定。不过自己随机了几组5000的数据都能过,没什么问题。
之后正解需要差分转化,但是后面涉及到SA我就不会了orz,把题解copy一下吧(听Dukelv说可以hash过?)
考虑两个串和谐的条件:a 1 -b 1 =...=a n -b n ,可以把它改为a 1 -a 2 =b 1 -b 2 ,a 2 -a 3 =b 2 -b 3 ...a n-1 -a n =b n-1 -b n 。所以我们可以把原串差分,然后就可以把它转成字符串匹配的问题。
考虑 Subtask 2,将差分后的串哈希,枚举两个串的起点,二分地求出它们最多能匹配几个雪人。复杂度 O(N 2 logN)
考虑 Subtask 3,求出差分过后的串的 sa 和 lcp,考虑二分答案,记当前二分的答案为 x。在 check 时,我们枚举一个 l,求出最大的 r 使得 min(lcp[l],lcp[l+1]...lcp[r])≥x,然后判断max(sa[l]...sa[r+1])-sa[l]是否≥x 即可,这可以用 set 和权值线段树实现。复杂度 O(Nlog 2 N)
考虑 Subtask 4,考虑 O(NlogN)地求出 sa,还是二分答案,其实在 check 的时候我们只需要考虑每一个 lcp[l]≥x...lcp[r]≥x 的串里 max(sa[l],...,sa[r+1])- min(sa[l],...,sa[r+1])是否
≥x 即可。复杂度 O(NlogN)。
当然,这个题也可以用 SAM 做,对于 SAM 上的每一个点,求 出 它 right 集 合 里 的 最 大 值 mx 和 最 小 值 mn , 用min(len+1,mx-mn)更新答案即可。
暴力代码:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #include<utility> #include<map> #include<ctime> #define pr pair<int,int> #define mp make_pair #define fi first #define sc second #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') using namespace std; typedef long long ll; const int M = 500005; const int N = 10000005; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >='0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int n,x,a[M],l,r,ans; bool pd(int p,int q,int x) { int k = a[p] - a[q]; rep(i,1,x-1) if((a[p+i] - a[q+i]) != k) return 0; // printf("%d %d ",p,q); return 1; } bool check(int x) { if(x > (n >> 1)) return 0; rep(i,1,n-(x<<1)+1) { rep(j,i+x,n-x+1) if(pd(i,j,x)) return 1; } return 0; } int main() { freopen("snowman.in","r",stdin); freopen("snowman.out","w",stdout); srand(time(NULL)); n = read(); rep(i,1,n) a[i] = read(); if(n <= 5000) { l = 1,r = n; while(l < r) { int mid = (l+r) >> 1; if(check(mid)) ans = mid,l = mid+1; else r = mid; } printf("%d ",ans); } else printf("%d ",rand()%n); return 0; } /* 10 1 2 3 4 5 6 7 8 9 10 */
std:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int s[1111111]; int n,rank[1111111],sa[1111111],k,fst[1111111],sec[1111111],h,lcp[1111111],x[1111111],l,r,mid; int cmp(int i,int j,int l) { if(i+l*2>n+1 || j+l*2>n+1) return 1; return sec[i]!=sec[j] || sec[i + l]!=sec[j + l]; } void make_sa() { for (int i=1; i<=n; i++) x[i]=s[i]; sort(x+1,x+1+n); int m=unique(x+1,x+1+n)-x-1; for (int i=1; i<=n; i++) s[i]=lower_bound(x+1,x+1+m,s[i])-x; memset(x, 0, sizeof(x)); for (int i=1; i<=n; i++) ++x[fst[i]=s[i]]; for (int i=1; i<=m; i++) x[i]+=x[i - 1]; for (int i=n; i>0; i--) sa[x[s[i]]--]=i; for (int i=1; i<n; i<<=1) { int p=0; for (int j=n-i+1; j<=n; j++) sec[++p] = j; for (int j=1; j<=n; j++) if(sa[j]>i) sec[++p]=sa[j] - i; memset(x,0,sizeof(x)); for (int j=1; j<=n; j++) ++x[fst[sec[j]]]; for (int j=1; j<=m; j++) x[j]+=x[j - 1]; for (int j=n; j>0; j--) sa[x[fst[sec[j]]]--]=sec[j]; memcpy(sec,fst,sizeof(sec)); fst[sa[1]]=m=1; for (int j=2; j<=n; j++) fst[sa[j]]=(m+=cmp(sa[j - 1],sa[j],i)); } } void make_lcp() { int h=0; for (int i=1; i<=n; i++) rank[sa[i]]=i; for (int i=1; i<=n; i++) { int j=sa[rank[i]-1]; if (h>0) h--; for (; i+h<=n && j+h<=n; h++) { if (s[i+h]!=s[j+h]) break; } lcp[rank[i]-1]=h; } } bool check(int x) { int pos=1,mn=1e9,mx=0; while(pos<n && lcp[pos]<x-1) pos++; while(pos<n) { mn=sa[pos]; mx=sa[pos]; while(pos<n && lcp[pos]>=x-1) { pos++; mn=min(mn,sa[pos]); mx=max(mx,sa[pos]); } while(pos<n && lcp[pos]<x-1) pos++; if (mx-mn>=x) return 1; } return 0; } int rpos, mmx; char str[10000000]; char readc() { if(!rpos) mmx = fread(str, 1, 10000000, stdin); return rpos == mmx ? 0 : str[rpos++]; } int read() { int x; char c; while((c=readc())<'0' || c>'9'); x=c-'0'; while((c=readc())>='0' && c<='9') x=x*10+c-'0'; return x; } int main() { freopen("snowman.in","r",stdin); freopen("snowman.out","w",stdout); n=read(); for (int i=1; i<=n; i++) x[i]=read(); for (int i=1; i<n; i++) { s[i]=x[i+1]-x[i]; } n--; make_sa(); make_lcp(); /*for(int i = 1; i <= n; i++) printf("%d ", s[i]); putchar(' '); for(int i = 1; i <= n; i++) printf("%d ", sa[i]); putchar(' '); for(int i = 1; i <= n; i++) printf("%d ", lcp[i]); putchar(' ');*/ l=0; r=n; while(l<=r) { mid=(l+r)>>1; if (check(mid)) l=mid+1; else r=mid-1; } printf("%d ",r); return 0; }