2021.8.9 模拟赛(搜索枚举
早上八点左右开考
十二点结束了
做题顺序:先把四道题都读了一遍,个人感觉第一题,第四题可能能写,第二题华容道我自己做都玩不好,你让我算最简步数?真亲亲高瞧我一眼。
大概是把第一题按暴力写了一部分,未必拿分但是是四道题里相对思路很全面的。再写一个dfs就把所有情况涵盖了但(会超时)我直接去看别的题了。
于是我就看了第四题。思路很清晰,但我搞了好几遍居然不太会输入?我服了我自己
lucky的是我发现第三题虽然我不会,但是25%的部分分可以写,于是我就写了。
第二题最后恒输出-1了,大概得不了分。
https://www.luogu.com.cn/blog/kanateta/post-202189-sou-suo-mei-ju-han-solution
题目↑
笑死我了,只有第二道得了十分
第一道题每组数据结束后没有更新统计量为0,更新后可得20分(笑
明天加油吧
先看看正解(确信
第一题
如果 n>=15,那么答案是 hzwer1+若干个 0(可能是 0 个)+数字 k-1 的字符串,比如对于 n=16,k=19260817 的情况:hzwer10019260816。 我们接下来做 n<=14 的情况,这只需要找出所有可能的 orz 与 hzwer 字符串 拼成的前缀+数字后缀组合即可——事实上可能的组合的种数并不多,可以手工 或写个简单的搜索列举出来,之后我们将这些有限的组合按照价值从小到大排序, 查找第 k 小的账号即可。
题解代码
#include<bits/stdc++.h> using namespace std; const int L1=14,M=50; struct prefix { string pre; int orz,hzwer; }p[M],q[M]; int m=0,T,n,k,a,b; bool prefix_cmp(const prefix &p1,const prefix &p2) { int v1=p1.hzwer*a+p1.orz*b; int v2=p2.hzwer*a+p2.orz*b; if(v1<v2) return true; if(v1>v2) return false; if(p1.orz<p2.orz) return true; if(p1.orz>p2.orz) return false; if(p1.hzwer<p2.hzwer) return true; if(p1.hzwer>p2.hzwer) return false; return p1.pre<p2.pre; } int num(int x) { if(x==0) return 1; int s=9; for(int i=2;i<=x;++i) s*=10; return s; } void get_prefix(string now,int o,int h,int oh) { if(h>=1) { ++m; p[m].pre=now; p[m].orz=oh; p[m].hzwer=h; } if((int)now.size()<=10) get_prefix(now+"hzwer",o,h+1,oh+o); if((int)now.size()<=12) get_prefix(now+"orz",o+1,h,oh); } int main() { freopen("fans.in","r",stdin); freopen("fans.out","w",stdout); get_prefix("",0,0,0);
scanf("%d",&T); while(T--) { scanf("%d%d%d%d",&n,&k,&a,&b); if(n>=15) printf("hzwer1%0*d\n",n-6,k-1); else if(n<=5) puts("-1"); else { int tot=0; for(int i=1;i<=m;++i) if(int(p[i].pre.size())<=n) q[++tot]=p[i]; sort(q+1,q+tot+1,prefix_cmp); bool flag=false; for(int i=1;i<=tot;++i) { int ll=n-int(q[i].pre.size()),tmp=num(ll); if(k>tmp) k-=tmp; else { flag=true; if(tmp==1) printf("%s\n",q[i].pre.c_str()); else { tmp/=9; printf("%s%d\n",q[i].pre.c_str(),tmp+k-1); } break; } } if(!flag) puts("-1"); } } }
第二题
一个是枚举滑块占据的范围,一个是移动,一个是状态 hash。 我们将所有棋子分为 11,12,21,22 四类,对于每类滑块,我们统一考虑 其占据的最左上角的位置,同时用坐标数组记录每类滑块占据的其它位置相对于 左上角的坐标,这样我们就可以通过滑块的左上角来处理滑块了。 移动的时候,我们先把当前滑块从局面上移走,之后再在相邻的地方放入, 这样就能比较好地解决滑块移动之后所占据的位置与移动之前有可能重叠的问 题,同时也不用为 12 和 21 滑块移动方向发愁。 状态 hash 比较简单,局面上每格的状态可以从原来的 8 种压缩为 5 种—— 空、被 11 占据,被 12 占据,被 21 占据,被 22 占据,这样压缩之后本质不 同的状态数就大大减少了。
题解代码
#include<bits/stdc++.h> using namespace std; const int N=7,M=6,x_[4]={0,1,0,-1},y_[4]={1,0,-1,0}; const int tx[5][4]={{},{0,0,0,0},{1,1,0,0},{1,1,0,0},{0,0,0,0}}; const int ty[5][4]={{},{0,0,0,0},{1,0,1,0},{0,0,0,0},{1,0,1,0}}; struct state { int a[N][M]; }s; int n=5,m=4,tr[8]={}; long long gethash(const state &s) { long long h=0; for(int i=n;i>=1;--i) for(int j=m;j>=1;--j) h=h*5+tr[s.a[i][j]]; return h; } bool check(const state &s) { return s.a[n-1][2]==2 && s.a[n-1][3]==2 && s.a[n][2]==2 && s.a[n][3]==2; } void bfs() { queue<state> q; queue<int> qd; set<long long> table; q.push(s); qd.push(0); table.insert(gethash(s)); if(check(s)) { puts("0"); return; } //int tot=0; while(!q.empty()) { state s=q.front(); q.pop(); int nowd=qd.front(); qd.pop(); //if((++tot)%1000==0) // cerr<<"tot="<<tot<<endl; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { int t=tr[s.a[i][j]],color=s.a[i][j]; if(t==0) continue; bool flag=true; for(int b=0;b<4;++b) flag=(flag && s.a[i+tx[t][b]][j+ty[t][b]]==s.a[i][j]); if(!flag) continue; for(int b=0;b<4;++b) s.a[i+tx[t][b]][j+ty[t][b]]=0; for(int d=0;d<4;++d) { int x=i+x_[d],y=j+y_[d]; bool ok=true; for(int b=0;b<4;++b) ok = (ok && s.a[x+tx[t][b]][y+ty[t][b]]==0); if(!ok) continue; for(int b=0;b<4;++b) s.a[x+tx[t][b]][y+ty[t][b]]=color; long long h=gethash(s); if(!table.count(h)) { q.push(s); qd.push(nowd+1); table.insert(h); if(check(s)) { printf("%d\n",nowd+1); return; } } for(int b=0;b<4;++b) s.a[x+tx[t][b]][y+ty[t][b]]=0; } for(int b=0;b<4;++b) s.a[i+tx[t][b]][j+ty[t][b]]=color; } } puts("-1"); } int main() { freopen("huarong.in","r",stdin); freopen("huarong.out","w",stdout); int T; cin>>T; while(T--) { for(int i=0;i<=n+1;++i) s.a[i][0]=s.a[i][m+1]=-1; for(int j=0;j<=m+1;++j) s.a[0][j]=s.a[n+1][j]=-1; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) cin>>s.a[i][j]; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { if(s.a[i][j]>=3) { if(s.a[i-1][j]==s.a[i][j] || s.a[i+1][j]==s.a[i][j]) tr[s.a[i][j]]=3; if(s.a[i][j-1]==s.a[i][j] || s.a[i][j+1]==s.a[i][j]) tr[s.a[i][j]]=4; } else tr[s.a[i][j]]=s.a[i][j]; } bfs(); } }
第三题
中位数,查找中位数,按定义找
题解代码
#include<bits/stdc++.h> using namespace std; const int x_[4]={0,1,0,-1},y_[4]={1,0,-1,0}; const int N=10,offset=N; const long long Inf=1ll<<60; int n,mx=0,my=0,px[N]={},py[N]={},fx[N]={},fy[N]={},ex[N*4]={},ey[N*4]={}; bool v[N+N][N+N]={}; void get_mid() { int x[N]={},y[N]={}; copy(fx+1,fx+n+1,x+1), copy(fy+1,fy+n+1,y+1); sort(x+1,x+n+1), sort(y+1,y+n+1); mx=(x[n/2+1]+x[(n+1)/2])/2, my=(y[n/2+1]+y[(n+1)/2])/2; } long long calc() { int per[N]={}; for(int i=1;i<=n;++i) per[i]=i; long long ans=Inf; do { long long s=0; for(int i=1;i<=n;++i) s+=abs(fx[per[i]]-px[i]), s+=abs(fy[per[i]]-py[i]); ans=min(ans,s); } while(next_permutation(per+1,per+n+1)); return ans; } long long tryy(int t,int s,int e) { if(t>n) return calc(); long long ans=Inf; for(int i=s;i<=e;++i) { px[t]=ex[i],py[t]=ey[i]; int ne=e; for(int d=0;d<4;++d) if(!v[px[t]+x_[d]+offset][py[t]+y_[d]+offset]) { ++ne; ex[ne]=px[t]+x_[d], ey[ne]=py[t]+y_[d]; v[ex[ne]+offset][ey[ne]+offset]=true; } ans=min(ans,tryy(t+1,i+1,ne)); for(int i=e+1;i<=ne;++i) v[ex[i]+offset][ey[i]+offset]=false; } return ans; } void init() { mx=my=0; for(int i=0;i<N;++i) px[i]=fx[i]=py[i]=fy[i]=0; cin>>n; for(int i=1;i<=n;++i) cin>>fx[i]>>fy[i]; get_mid(); for(int i=1;i<=n;++i) fx[i]-=mx, fy[i]-=my; } void work() { v[0+offset][0+offset]=true; for(int i=1;i<=4;++i) { ex[i]=x_[i-1],ey[i]=y_[i-1]; v[ex[i]+offset][ey[i]+offset]=true; } cout<<tryy(2,1,4)<<endl; } int main() { freopen("meet.in","r",stdin); freopen("meet.out","w",stdout); int T; cin>>T; for(int i=1;i<=T;++i) { init(); work(); } fclose(stdin); fclose(stdout); return 0; }
第四题
分两种情况: 一种是没人说了第一种话,这时候直接枚举第一个人说的话是真是假,然后 check 就行了。 另一种情况下,我们可以枚举一共有几个人说了真话,那么现在对于所有说 了第一种话的人,我们都可以判断真假,反推并 check 即可。 注意到在枚举几个出题人说了真话时,对每个说了第一种话的人,其反推出 来的结果只有两种,因此可以对每段预处理一下。
题解代码
#include<bits/stdc++.h> using namespace std; inline char getc() { char ch; while(isspace(ch=getchar())) ; return ch; } inline int getint() { char ch; while(!isdigit(ch=getchar())) ; int x=ch-'0'; while(isdigit(ch=getchar())) x=x*10+ch-'0'; return x; } const int N=100010; struct block { int s,num,len,t[2],f[2]; }p[N]={}; bool block_cmp(const block &b1,const block &b2) { return b1.num<b2.num; } int n,m,a[N]={},b[N]={}; char ans[N]={}; void init() { fill(p+1,p+m+1,(block){0,0,0,{0,0},{0,0}}); m=0; fill(a+1,a+n+1,0),fill(b+1,b+n+1,0); fill(ans+1,ans+n+1,'\0'); n=getint(); for(int i=1;i<=n;++i) { char ch=getc(); if(ch=='+') a[i]=1; if(ch=='-') a[i]=2; if(ch=='$') b[i]=getint(); } } void get_block(int now) { ++m; p[m].s=now; int next=1; while(a[now]) { p[m].t[0]+=next==1; next=next==1 ? a[now] : 3-a[now]; now=now%n+1; ++p[m].len; } p[m].num=b[now]; p[m].f[0]=next; p[m].t[1]=p[m].len-p[m].t[0]; p[m].f[1]=3-p[m].f[0]; } void mark(int now,int next) { while(a[now]) { ans[now]=next==1 ? 't' : 'f'; next=next==1 ? a[now] : 3-a[now]; now=now%n+1; } ans[now]=next==1 ? 't' : 'f'; } void work() { for(int i=1;i<=n;++i) if(a[i]==0) get_block(i%n+1); if(m==0) { int next=1,con=next==a[1] ? 1 : 2; ans[1]='t'; repeat: for(int i=2;i<=n;++i) { ans[i]=next==1 ? 't' : 'f'; next= (next==1) ? a[i] : 3-a[i]; } if(next!=con) { if(ans[1]=='f') puts("inconsistent"); else { ans[1]='f'; next=2; con=next==a[1] ? 1 : 2; goto repeat; } } else { ans[1]=next==2 ? 'f' : 't'; puts("consistent"); puts(ans+1); } } else { sort(p+1,p+m+1,block_cmp); int t=0; for(int i=1;i<=m;++i) t+=p[i].f[0]==2 ? p[i].t[0] : p[i].t[1]; bool flag=true; for(int i=1;i<=m && flag;++i) if(t==p[i].num) flag=false; if(flag) { puts("consistent"); for(int i=1;i<=m;++i) mark(p[i].s,p[i].f[0]==2 ? 1 : 2); puts(ans+1); } for(int i=1,r=1;i<=m && !flag;i=r) { while(r<=m && p[r].num==p[i].num) ++r; for(int j=i;j<r;++j) { t+=p[j].f[0]==1 ? p[j].t[0] : p[j].t[1]; t-=p[j].f[0]==2 ? p[j].t[0] : p[j].t[1]; } if(t+r-i==p[i].num) { puts("consistent"); for(int j=1;j<=m;++j) if(i<=j && j<r) mark(p[j].s,p[j].f[0]==1 ? 1 : 2); else mark(p[j].s,p[j].f[0]==2 ? 1 : 2); for(int j=1;j<=n;++j) putchar(ans[j]); putchar('\n'); flag=true; } for(int j=i;j<r;++j) { t-=p[j].f[0]==1 ? p[j].t[0] : p[j].t[1]; t+=p[j].f[0]==2 ? p[j].t[0] : p[j].t[1]; } } if(!flag) puts("inconsistent"); } } int main() { int T=getint(); while(T--) { init(); work(); } return 0; }

浙公网安备 33010602011771号