集训日记:DAY1
正好教练让每天写总结,就开个新的日记专栏,每天总结一下
今天早上满怀希望的想清华集训大佬zld能给我们讲点什么,结果进门就撞见一个模拟赛……
说是什么摸摸底,还考搜索。
鉴于之前搜索模拟赛的经验,我带着AK的蜜汁自信打开了题面。
先看T1 hzw的粉丝
好家伙,这人叫hzw?我叫hwz?(雾
题面吧啦吧啦一大顿简而言之就是要构造一个字符串,使得大小为第k小。
于是我的脑海中:搜索+字符串构造=全排列+kmp……(此时过去了10min)
初步有想法后去看T2华容道
一眼看到这个题就觉得不可做,整块移动不会处理,固输-1就完了,管他有没有分,真男人从不输入 (此时过去15min)
再看T3聚会
这道题我的想法是从每个点开始bfs往外括展,到第几层把所有的点覆盖了就跳出,然后取个min。
一看数据范围-10e9<x,y<10e9???有点难办呀……(此时过去25min)
没细想去看T4真相
这个题搜索的话可以枚举每个人的对错?或者……差分约束?对于$的限定再加一些奇怪的判断???
(此时过去30min)
把所有题都大概看一遍之后(这个策略来自于我闲着没事看的诸多NOI游记),我以为一句话
决定先开T1
T1中n<=5无解的情况秒出,然后搜索写炸了,死活re,好不容易调通了输出又不对,这时候才知道自己的代码能力原来如此之弱……lyn学长说得真tm真实 这么一忙活1h就过去了,然后着急忙慌放下这个题去写T3(T2五分钟写完固输,到现在看完题解仍然认为不可做)(此时过去1.5h)
注:这里我有一个策略失误,即不应该先开最有把握的题(实际上也没什么把握)(手动删除线),而且我不应该把时间均匀分配给每一道题,而是要有侧重。
T3暴搜倒是和正解不谋而合,但是中位线这块没想到。广搜过程中数组下标问题没有处理好……样例过不去,这时候心理有点慌,觉得可能要爆零……(此时过去3h)
再去写T4,在暴力枚举和差分约这两个思路中我错误地选择了差分约束…… 毕竟更好写
然后建图半天建不出来, $情况特判不会写,加上就剩1h不太够用,没写完就交了……
交的时候想固输可能还有分,结果因为之前某模拟赛固输样例切题的经历,我固输了样例……
所以真就爆零。
爆零还挺难受的,毕竟像省选,NOI同步赛这样难得多的比赛我都没爆零……所以说模拟赛考得没我省选分高,所以模拟赛难度>省选,≈NOI;又因为今天模拟赛考得没我NOI同步赛分高,所以模拟赛难度>NOI,≈IOI,因为我IOI爆零很正常,所以今天模拟赛我爆零很正常???
但是看到有一半爆零的,其余基本10-30pts,最高55pts还能好受一点。
但是爆零没法挽回,经验要有长进。
这次模拟我自己总结有5大问题:
- 平时做题过于依赖题解,脑子里想会了就懒得写,导致代码能力弱。
- 考试时开题顺序有误,应先写暴力,再写有把握的题
- 时间分配应有所侧重
- 考试时心态易受他人影响,应心无旁骛(向傍边杜佬学习
虽然他今天机惨我我现在像把他“杀”了——淡定看题1.5h,淡定写题2.5h - 考试不要老想着切题,暴力分打满一样很客观
今天事实证明好像我连暴力都不会???
至于考试流一个小时(2h-3h)鼻血导致头晕之类的客观原因就不找了……
最后贴上正解和标程
T1 hzw的粉丝
显然,如果 n>=15,那么答案是 hzwer1+若干个 0(可能是 0 个)+数字 k-1 的字符串,比如对于 n=16,k=19260817 的情况:hzwer10019260816。 我们接下来做 n<=14 的情况,这只需要找出所有可能的 orz 与 hzwer 字符串 拼成的前缀+数字后缀组合即可——事实上可能的组合的种数并不多,可以手工 或写个简单的搜索列举出来,之后我们将这些有限的组合按照价值从小到大排序, 查找第 k 小的账号即可。
std:
#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);
//for(int i=1;i<=m;++i)
// cout<<"str="<<p[i].pre<<" orzhzwer="<<p[i].orz<<" hzwer="<<p[i].hzwer<<endl;
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");
}
}
}
听说这题赛后有人打表切了???就离谱……
T2华容道游戏
很显然这是道 BFS 的裸题,这题主要的地方在于实现。 一个是枚举滑块占据的范围,一个是移动,一个是状态 hash。 我们将所有棋子分为 11,12,21,22 四类,对于每类滑块,我们统一考虑 其占据的最左上角的位置,同时用坐标数组记录每类滑块占据的其它位置相对于 左上角的坐标,这样我们就可以通过滑块的左上角来处理滑块了。 移动的时候,我们先把当前滑块从局面上移走,之后再在相邻的地方放入, 这样就能比较好地解决滑块移动之后所占据的位置与移动之前有可能重叠的问 题,同时也不用为 12 和 21 滑块移动方向发愁。 状态 hash 比较简单,局面上每格的状态可以从原来的 8 种压缩为 5 种—— 空、被 11 占据,被 12 占据,被 21 占据,被 22 占据,这样压缩之后本质不 同的状态数就大大减少了。 如果对于相关实现有疑问,可以参考标程。
std:
#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();
}
}
仍然认为不可做
T3聚会
#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;
}
中位数啊喂!!!
T4真相
分两种情况: 一种是没人说了第一种话,这时候直接枚举第一个人说的话是真是假,然后 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;
}