—————————————- 牢骚 ————————————————-
今天的测验依旧爆炸垫底。难过归难过,究其原因是时间分配有问题。
大概是被雅礼的题虐惯了,看到第一题就想跳,今天全程没有看第一题我都不知道自己怎么想的,结果第一题实际上非常简单,成了全场唯一没有做的人。
花费了三个小时,想去得第二题的40分,最后一个小时匆匆写了一三题的暴力,结果第二题打的40分的表cena收不上去,mmp。
过后来看,觉得自己脑子真是不清楚…
我可能需要再考几次试才能摸清四个半小时应该怎么用,感觉现在还是一团浆糊。但是大家似乎都已经适应了,唉。
2月8日的测验:
T1.runner
稀里糊涂地就放掉了,实际上只需要分类讨论:
1.在不同子树内
2.在相同子树内
但是代码有些繁琐。
如果在考场上做这道题的话,关于在不同子树内的情况,
一个是,一个是这两种可能想不清楚。
第二个是代码不短,调试难度还是有。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100005;
const int inf=0x3f3f3f3f;
int n,head[N],to[2*N],nxt[2*N],num=0,C[N],Top=0,top=0,S[N],mx[N],mxer[N],dis[N];
int mxlen=0,st=0,ed=0,ans=inf;
bool vis[N],ins[N],flag=0,onc[N];
void build(int u,int v)
{
num++;
to[num]=v;
nxt[num]=head[u];
head[u]=num;
}
void getcircle(int u,int f)
{
if(flag) return;
vis[u]=1; ins[u]=1; S[++Top]=u;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i]; if(v==f) continue;
if(ins[v])
{
flag=1;
while(1)
{
C[++top]=S[Top]; onc[S[Top]]=1;
ins[S[Top]]=0;
Top--;
if(S[Top+1]==v) break;
}
}
else if(vis[v]) continue;
else getcircle(v,u);
}
if(Top) Top--,ins[u]=0;
}
bool check(int len,int s,int t,int opt)
{
if(opt)
{
if(len>mxlen) return 1;
if(len==mxlen&&s<st) return 1;
if(len==mxlen&&s==st&&t<ed) return 1;
}
else
{
if(len<ans) return 1;
if(len==ans&&s<st) return 1;
if(len==ans&&s==st&&t<ed) return 1;
}
return 0;
}
void dfs(int u,int f)
{
mx[u]=0,mxer[u]=u;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(onc[v]||v==f) continue;
dfs(v,u);
int len=mx[v]+mx[u]+1; int s=mxer[u],t=mxer[v];
if(s>t) swap(s,t);
if(check(len,s,t,1)) mxlen=len,st=s,ed=t;
if(mx[u]<mx[v]+1||(mx[u]==mx[v]+1&&mxer[u]>mxer[v])) mx[u]=mx[v]+1,mxer[u]=mxer[v];
}
}
void getans()
{
for(int i=2;i<=top;i++) dis[C[i]]=dis[C[i-1]]+1;
//----opt 1. 2(n-1)-(dis[now]-dis[pre])-mx[now]-mx[pre];
// =2(n-1)-dis[now]-mx[now]+dis[pre]-mx[pre]
int L=-mx[C[1]]; int x=mxer[C[1]];// dis[pre]-mx[pre]
for(int i=2;i<=top;i++)
{
int u=C[i]; int s=mxer[u],t=x;
int len=2*(n-1)+L-dis[u]-mx[u];
if(s>t) swap(s,t);
if(check(len,s,t,0)) ans=len,st=s,ed=t;
if(dis[u]-mx[u]<L||(dis[u]-mx[u]==L && mxer[u]<x)) L=dis[u]-mx[u],x=mxer[u];
}
//----opt 2. 2(n-1)-(top-(dis[now]-dis[pre]))-mx[now]-mx[pre];
// =2(n-1)+dis[now]-mx[now]-dis[pre]-mx[pre]-top
L=-mx[C[1]],x=mxer[C[1]];// -dis[pre]-mx[pre]
for(int i=2;i<=top;i++)
{
int u=C[i]; int s=mxer[u],t=x;
int len=2*(n-1)+L+dis[u]-mx[u]-top;
if(s>t) swap(s,t);
if(check(len,s,t,0)) ans=len,st=s,ed=t;
if(-dis[u]-mx[u]<L||(-dis[u]-mx[u]==L && mxer[u]<x)) L=-dis[u]-mx[u],x=mxer[u];
}
}
int main()
{
freopen("runner.in","r",stdin);
freopen("runner.out","w",stdout);
scanf("%d",&n); ans=inf;
for(int i=1;i<=n;i++)
{
int u,v; scanf("%d%d",&u,&v);
build(u,v); build(v,u);
}
memset(ins,0,sizeof(ins));
getcircle(1,1);
//---get the deepest point in each tree
//---S and T in one tree
for(int i=1;i<=top;i++) dfs(C[i],C[i]);
ans=min(ans,(2*n-mxlen-top));
//---S ans T in different trees
getans();
printf("%d %d %d\n",ans,st,ed);
return 0;
}
T2.longer
这是最亏的。
斜率优化要想到肯定是很容易,但是认为那样的复杂度可卡所以没有写,考试时花了大把时间去想有没有其他合理的做法,结果std就写的那个,mmp。
但是实际上在没有其他想法的时候,这种可以骗骗分的思路是可以写的。同时,在考场上大家都写了的那个理论复杂度有问题的做法,下来讨论发现那个做法确实是难以卡掉的。
如果要严谨的方法,可以二分弹栈的位置,决策点也可以二分去找。(最后想到了二分,但是没有想清楚,而且也没有时间了)
T3.knightmov
目前还不知道正解是什么,考场上大部分时间给了T2,最后写了T3。
我的想法是容斥,但是由于时间不够以及暂时还没有想到很多细节如何处理,我的判无解和无穷解的机制是有问题的,它会把一些正常情况也判成无解和无穷解。最后得了45’。
2月9日的测验:
实在是应该好好反省自己的考试时间规划。
T1.yist
实际上是一道比较简单的题,和之前做过的bzoj3771有些像。
但是考场上下意识就跳过了直接从T2开始看。
教训:要认真看过每道题的题面,在草稿纸上画一画有没有初步想法之后再决定从哪题开始,不能习惯性地/随性地安排顺序,头脑最初比较清晰的时候的安排,一定要慎重。
6=3+1+1+1
6=2+2+1+1
一个比较简单的处理就是安排顺序,例如先按从小到大排序,这样容斥起来非常有条理。
下午改题的时候没看sol自己想了做了,大概花了一个半小时多,中间差点漏了ab cd和ac bd会重复的情况。但是最终在讨论时还是少了一个细节就是2+2是四个相同的情况,所以第一次改是70’,静态查出来加上了。实际上这还是又忘了要先仔细静态查错这件事。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const int MXN=10000007;
const int N=5005;
/*
6=3+1+1+1
6=2+2+1+1
*/
int one[MXN],two[2*MXN],a[N],n;
LL ans1=0,ans2=0;
LL C(int n,int m)
{
if(n<m) return 0;
LL ret=1;
for(int i=n;i>=n-m+1;i--) ret=1LL*ret*i;
for(int i=2;i<=m;i++) ret/=(1LL*i);
return ret;
}
int main()
{
freopen("yist.in","r",stdin);
freopen("yist.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),one[a[i]]++; //一个的
sort(a+1,a+n+1); //get a
for(int i=1;i<=n;i++) for(int j=1;j<i;j++) two[a[i]+a[j]]++; //两个的
int last=0;
for(int i=1;i<=n;i++)
{
if(a[i]==last) continue; int x=a[i]; //ai是第一个等于s的
LL ret=0;
if(one[x]>=3) //3 1 1 1
{
for(int j=1;j<i;j++) //选了a[j]; 不同的三元组会被枚举3次
{
ret+=1LL*two[x-a[j]];//abc*3
if(x>=2*a[j]) ret-=1LL*(one[x-a[j]-a[j]]-(3*a[j]==x)); //abb
}
ret/=3LL;
ans1+=1LL*ret*C(one[x],3);
}
if(one[x]>=2) //2 2 1 1 ab cd
{
int La=0; ret=0; LL cnt=0;
for(int j=i-1;j>=1;j--)
{
if(a[j]==La) continue;
if(x-a[j]>a[j]) break;
LL cur=0;
if(a[j]!=x-a[j]) cur=(1LL*one[a[j]]*one[x-a[j]]),ret+=(1LL*C(one[a[j]],2)*C(one[x-a[j]],2));
else cur=C(one[a[j]],2),ret+=1LL*C(one[a[j]],4);
ret+=(1LL*cur*cnt);
cnt+=cur;
La=a[j];
}
ans2+=1LL*ret*C(one[x],2);
}
last=a[i];
}
printf("%I64d\n",ans1+ans2);
return 0;
}
T2.num
花了几乎所有时间而几乎没有分的一道题。
最初决定先做这道题的原因很简单:打表找规律先
谁知不知不觉就在上面耗完了考试时间。
确实,要随时提醒自己不要在一个状态上面陷得过久。
而我打表找规律得到的是,每一行是杨辉三角的n倍,因此后面的处理都直接按杨辉三角想了,
也就因此,难以看出那个至关重要的规律,因为它是针对原三角形(莱布尼兹调和三角的分母)的:
题目条件每一个都可能有用,不要轻易抛掉哪个。
不是没想到数据结构来维护各个质数的次数,但是没有这个转化还是很难,用40’算法对前40’打了个表,程序交的是表,但是cena居然收不上去,惨痛。
这个维护的方法还是比较巧妙的。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
const int mod=1000000007;
using namespace std;
const int N=100000;
int ptot=0,P[N+5],pri[N+5],is[N+5];
vector<int> pos[N+5],num[N+5],po[N+5];
int tail=0,root[N+5],val[N+5];
int Q,Ni,Ki,A,B,M,C[2*N+5],D[2*N+5];
struct node
{
int ls,rs,sum;
void init(){ls=rs=0,sum=1;}
}tr[N*100+5];
void shai()
{
is[1]=1;
for(int i=2;i<=N;i++)
{
if(!is[i]) pri[++ptot]=i,P[i]=i;
for(int j=1;j<=ptot;j++)
{
int pp=pri[j];
if(1LL*pp*i>N) break;
is[pp*i]=1;
P[pp*i]=pp;
if(i%pp==0) break;
}
}
}
void insert(int pre,int &now,int lf,int rg,int pos)
{
now=++tail,tr[now]=tr[pre];
if(lf==rg) {tr[now].sum=val[lf]; return;}
int mid=(lf+rg)>>1;
if(pos<=mid) insert(tr[pre].ls,tr[now].ls,lf,mid,pos);
else insert(tr[pre].rs,tr[now].rs,mid+1,rg,pos);
tr[now].sum=(1LL*tr[tr[now].ls].sum*tr[tr[now].rs].sum)%mod;
}
int query(int nd,int lf,int rg,int L,int R)
{
if(L<=lf&&rg<=R) return tr[nd].sum;
int mid=(lf+rg)>>1;
int ret=1;
if(L<=mid) ret=(1LL*ret*query(tr[nd].ls,lf,mid,L,R))%mod;
if(R>mid) ret=(1LL*ret*query(tr[nd].rs,mid+1,rg,L,R))%mod;
return ret;
}
int main()
{
freopen("num.in","r",stdin);
freopen("num.out","w",stdout);
shai();
root[0]=0;tr[0].init();
val[1]=1; insert(root[0],root[1],1,N,1);
for(int i=2;i<=N;i++)
{
int tmp=i; val[i]=i;
insert(root[i-1],root[i],1,N,i);
while(tmp!=1)
{
int pi=P[tmp],cnt=0,mi=1,Mi,Cnt;
for(;tmp%pi==0;tmp/=pi) cnt++,mi*=pi; Mi=mi,Cnt=cnt;
int sz=pos[pi].size();
for(int j=sz-1;j>=0;j--)
{
if(!cnt) break;
if(num[pi][j]<=cnt)
{
val[pos[pi][j]]/=po[pi][j];
insert(root[i],root[i],1,N,pos[pi][j]);
cnt-=num[pi][j];
mi/=po[pi][j];
pos[pi].pop_back();
po[pi].pop_back();
num[pi].pop_back();
}
else
{
val[pos[pi][j]]/=mi;
insert(root[i],root[i],1,N,pos[pi][j]);
po[pi][j]/=mi;
num[pi][j]-=cnt;
break;
}
}
pos[pi].push_back(i);
po[pi].push_back(Mi);
num[pi].push_back(Cnt);
}
}
scanf("%d",&Q);
scanf("%d%d",&Ni,&Ki);
scanf("%d%d%d",&A,&B,&M);
for(int i=1;i<Q;i++) scanf("%d",&C[i]);
for(int i=1;i<Q;i++) scanf("%d",&D[i]);
int ans=query(root[Ni],1,N,Ni-Ki+1,Ni);
printf("%d\n",ans);
for(int i=1;i<Q;i++)
{
Ni=(1LL*A*ans%M+C[i]%M)%M+1;
Ki=(1LL*B*ans%Ni+D[i]%Ni)%Ni+1;
ans=query(root[Ni],1,N,Ni-Ki+1,Ni);
printf("%d\n",ans);
}
return 0;
}
T3.sanrd
题解写的比较草率,没看懂。
考试方面,我觉得自己的问题问题非常大。
时间安排这方面需要再仔细分以下,暂定:
7:40-8:10读题
不能到9:10为止还不开始做题。
9:10如果可能去切第一题或者正在切第一题。
写完一题拍着走。
之后想清楚有没有时间T2,T3都涉猎,不然集中选一道攻克,剩下的也要写暴力
最后留时间至少半小时检查。
真的觉得自己码力不行。现在不能凌晨起来,不过寒假的话,只要有CF的比赛都打算去打。
最近心情不佳。
每天还是挺孤独的,而且对自己没什么自信吧,还是挺难受。有时候真想痛痛快快哭一场。
不过,就算现在天天垫底也不要被打倒!
总之,做好自己手上的事,做好能做的事总会有进步的!
加油!