5.27 考试修改+总结
这是一个悲伤的故事
上午写manacher的时候往里面加#号,然后统计有效字符的个数
然后我就开始模拟,一个长度为6的串我都能数错有多少个有效字符
我把2个字符数成了3个!然后暴力就挂掉了5分。。
为什么这几天的暴力总是会挂掉,真是奇怪(看来是最近自己内心不太稳了
(大概是被那个梦吓得吧QAQ)
今天又A了两道题目,感觉天天都是两道正解+挂掉的暴力QAQ
先放题解吧
第一题是之前数位DP坑掉的题目,然后今天尝试着写了写,感觉不是很难
但是并不是用数位DP做的
先考虑加密的情况,我们只需要统计每一位0,1出现的次数就可以了
然后这个随便搞一搞就能搞出来
考虑不加密的情况,不难发现如果从某一位开始不贴上界的话,那么后面都可以通过^变成1
然后分情况讨论这一位上界是0和这一位上界是1就可以了,模拟一下小数据就可以写出来了
写完之后以为题目的1e-5是绝对误差,然后就开始疯狂调精度
最后差不多差距1e-2,然后发现题目说的是相对误差QAQ 浪费了好长时间
得到了一些奇怪的调精度的方法,譬如输出long double的时候强制转成double会损失些精度
我们可以先用long long输出整数位,然后转double输出小数位
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> #define eps 1e-8 using namespace std; typedef long long LL; LL n; double k; long double p; long double p1,p2; long double A,B,C; int Num[72],len=0; LL f[72][2]; LL suf[72]; void check(LL n){ memset(Num,0,sizeof(Num));len=0; if(!n){Num[++len]=0;return;} while(n)Num[++len]=(n&1),n>>=1; } bool judge(){ for(int i=1;i<=len;++i)if(Num[i]==0)return false; return true; } int main(){ //freopen("news.in","r",stdin); //freopen("news.out","w",stdout); scanf("%lld",&n); scanf("%lf",&k);p=k; n--;check(n); if(judge()){ if(fabs(p-1)<eps){ long double ans=n; LL tmp=(LL)(ans); printf("%lld.",tmp); ans=ans-tmp;ans*=1e10; printf("%010.0lf\n",(double)(ans)); return 0; }else if(fabs(p)<eps){ long double ans=n; ans/=2.0; LL tmp=(LL)(ans); printf("%lld.",tmp); ans=ans-tmp;ans*=1e10; printf("%010.0lf\n",(double)(ans)); return 0; } } n++; LL tmp=1; for(int i=1;i<len;++i)tmp=tmp+(1LL<<(i-1))*Num[i]; A=(long double)(tmp);p1=p1+(A/n)*(long double)((1LL<<len)-1); C=(long double)(1LL<<(len-1));p1=p1+(C/(long double)(n))*C;C/=(long double)(n); for(int i=len-1;i>=1;--i){ if(Num[i]==1){ C*=0.5; p1=p1+C*(long double)((1LL<<i)-1); p1=p1+C*(long double)(1LL<<(i-1)); }else{ A=C*0.5; p1=p1+A*(long double)(1LL<<(i-1)); } } suf[0]=1; for(int i=1;i<=len;++i)suf[i]=suf[i-1]+(1LL<<(i-1))*Num[i]; for(int i=len;i>=1;--i){ if(Num[i]==1){ LL tmp=(1LL<<(i-1)); f[i][0]+=tmp;tmp>>=1; for(int j=i-1;j>=1;--j){ f[j][0]+=tmp; f[j][1]+=tmp; } f[i][1]+=suf[i-1]; }else f[i][0]+=suf[i-1]; } for(int i=1;i<=len;++i){ A=(long double)(f[i][0]);A/=(long double)(n); B=(long double)(f[i][1]);B/=(long double)(n); C=(long double)(1LL<<(i-1)); p2=p2+A*B*C*2.0; } p=p1*p+p2*(1.0-p); tmp=(LL)(p); printf("%lld.",tmp); p-=tmp;p*=1e10; printf("%010.0lf\n",(double)(p)); return 0; }
第二题是BZOJ的原题,久仰大名但是从来没写过
今天一看发现是丝薄题,五分钟就秒掉了
然后写了大概20min左右拍上了,卡了卡常数就没有管
首先我们考虑b是a的祖先,那么b有多少个很容易算,满足条件的c就是a的子树大小-1
之后考虑a是b的祖先,我们先思考一下如果没有k的限制,这样的话类似HAOI2015 T1化一下式子算贡献就可以了
具体是这样的:每个点x作为c对a的答案贡献是(dep[x]-dep[a]-1)
因为dep[a]+1和子树大小都是常数,我们只需要用线段树统计子树dep的和就可以了
考虑k的限制,不难发现实际上是给这个题目加了一维深度,即统计贡献的x在dep的某个区间里,其余的不在这个区间的x的贡献都是k
把线段树换成可持久化线段树就可以了
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<iostream> using namespace std; typedef long long LL; const int maxn=300010; int n,m,u,v,now,k; int h[maxn],cnt=0; struct edge{ int to,next; }G[maxn<<1]; int pos[maxn],ed[maxn],tot=0; int dep[maxn],siz[maxn]; int tmp[maxn]; int rt[maxn],sum=0; struct Seg_Tree{ int L,R,v; LL S; }t[11000010]; bool cmp(const int &x,const int &y){ return dep[x]>dep[y]; } void add(int x,int y){ ++cnt;G[cnt].to=y;G[cnt].next=h[x];h[x]=cnt; } void read(int &num){ num=0;char ch=getchar(); while(ch<'!')ch=getchar(); while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar(); } void Get_DFS(int u,int f){ pos[u]=++tot;siz[u]=1; for(int i=h[u];i;i=G[i].next){ int v=G[i].to; if(v==f)continue; dep[v]=dep[u]+1; Get_DFS(v,u); siz[u]+=siz[v]; }ed[u]=tot;return; } void build(int &o,int L,int R){ o=++sum; if(L==R)return; int mid=(L+R)>>1; build(t[o].L,L,mid); build(t[o].R,mid+1,R); } void UPD(int &o,int L,int R,int p,int v){ t[++sum]=t[o];o=sum; if(L==R){t[o].v++;t[o].S+=v;return;} int mid=(L+R)>>1; if(p<=mid)UPD(t[o].L,L,mid,p,v); else UPD(t[o].R,mid+1,R,p,v); t[o].v=t[t[o].L].v+t[t[o].R].v; t[o].S=t[t[o].L].S+t[t[o].R].S; } int ask_v(int o,int L,int R,int x,int y){ if(L>=x&&R<=y)return t[o].v; int mid=(L+R)>>1; if(y<=mid)return ask_v(t[o].L,L,mid,x,y); else if(x>mid)return ask_v(t[o].R,mid+1,R,x,y); else return ask_v(t[o].L,L,mid,x,y)+ask_v(t[o].R,mid+1,R,x,y); } LL ask_S(int A,int B,int L,int R,int x,int y){ if(L>=x&&R<=y)return t[A].S-t[B].S; int mid=(L+R)>>1; if(y<=mid)return ask_S(t[A].L,t[B].L,L,mid,x,y); else if(x>mid)return ask_S(t[A].R,t[B].R,mid+1,R,x,y); else return ask_S(t[A].L,t[B].L,L,mid,x,y)+ask_S(t[A].R,t[B].R,mid+1,R,x,y); } int main(){ //freopen("laugh.in","r",stdin); //freopen("laugh.out","w",stdout); read(n);read(m); for(int i=1;i<n;++i){ read(u);read(v); add(u,v);add(v,u); }dep[1]=1;Get_DFS(1,-1); for(int i=1;i<=n;++i)tmp[i]=i; sort(tmp+1,tmp+n+1,cmp);now=1; build(rt[n+1],1,n); for(int i=n;i>=1;--i){ rt[i]=rt[i+1]; while(now<=n&&dep[tmp[now]]==i){ u=tmp[now]; UPD(rt[i],1,n,pos[u],dep[u]); now++; } } while(m--){ read(u);read(k); LL Av,AS,Bv,BS; LL t1=siz[u]-1; LL t2=min(dep[u]-1,k); LL ans=t1*t2; now=dep[u]+k+1; if(now>n)Av=0; else Av=ask_v(rt[now],1,n,pos[u],ed[u]); BS=ask_S(rt[dep[u]+1],rt[now],1,n,pos[u],ed[u]); Bv=t1-Av; ans=ans+Av*k; ans=ans+BS; ans=ans-Bv*(dep[u]+1); printf("%lld\n",ans); } return 0; }
第三题考场上犯逗,暴力挂掉了5分QAQ
首先如果存在一个串是回文串,答案是inf
之后我们考虑一个回文子串要满足什么条件可以扩展,显然这个回文子串要有一侧碰到边界
那么对于这个串就会剩下右半部分或者左半部分
我们拿别的串去拼剩下的部分就可以了
考虑用图论模型来描述这个概念,我们对于每一个位置用两个点分别表示剩下左边和剩下右边
用边来表示可拼接的转移
我们新建一个节点源点向每个碰到边界的回文子串的另一个边界点建边
注意空串也是一个回文串,所以也要额外建边
之后我们考虑串与串之间的转移情况,设A表示有回文子串的串,B表示另一个用来拼接的串
分类讨论如下:
1、拼接后都不碰到边界,显然不可在扩展,为了方便统计答案我们向汇点建边
2、拼接后A碰边界,B不碰,A的点向B的对应点建边
3、拼接后A不碰边界,B碰边界,A的点向A中匹配B之后的点建边,表示还可以继续匹配
4、都碰边界,A和B形成了一个回文串,答案为inf
之后我们得到了一个图,对于这个图由于每条边都表示一个可行的转移
即如果这个图有环那么答案为inf
否则从S求最长路即为答案
至于判断是否可拼接用字符串相关的数据结构随便就搞定了
本来想自己写写练练码力,结果写完之后发现一堆边界搞错,下标搞混的问题
这真是一个悲伤的故事
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; const int maxn=200010; int n,m,h[maxn],cnt,blo; int bin[20],lgl[maxn]; int len[maxn],Slen[maxn]; int L[maxn],R[maxn]; int sa[maxn],rk[maxn],height[maxn]; int st[20][maxn]; int wa[maxn],wb[maxn],wv[maxn],tmp[maxn]; int s[maxn]; struct edge{ int to,next,w; }G[30000010]; void add_edge(int x,int y,int z){ ++cnt;G[cnt].to=y;G[cnt].next=h[x];G[cnt].w=z;h[x]=cnt; } void Get_sa(int *r, int n, int m) { int *x=wa,*y=wb; for(int i=0;i<m;++i)tmp[i]=0; for(int i=0;i<n;++i)tmp[x[i]=r[i]]++; for(int i=1;i<m;++i)tmp[i]+=tmp[i-1]; for(int i=n-1;i>=0;--i)sa[--tmp[x[i]]]=i; for(int j=1;j<n;j<<=1){ int p=0; for(int i=n-j;i<n;++i)y[p++]=i; for(int i=0;i<n;++i)if(sa[i]>=j)y[p++]=sa[i]-j; for(int i=0;i<m;++i)tmp[i]=0; for(int i=0;i<n;++i)tmp[wv[i]=x[y[i]]]++; for(int i=1;i<m;++i)tmp[i]+=tmp[i-1]; for(int i=n-1;i>=0;--i)sa[--tmp[wv[i]]]=y[i]; swap(x,y); p=1;x[sa[0]]=0; for(int i=1;i<n;++i){ x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++; } if(p>=n)break; m=p; } } void Get_height(int *r,int n){ int i,j,k; for(i=j=k=0;i<n;height[rk[i++]]=k){ for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++); }return; } void Get_ST(int n){ for(int i=1;i<=n;++i)st[0][i]=height[i]; for(int i=1;i<=lgl[n];++i){ for(int j=1;j+bin[i]-1<=n;++j){ st[i][j]=min(st[i-1][j],st[i-1][j+bin[i-1]]); } }return; } int ST_ask(int x,int y){ x=rk[x];y=rk[y]; if(x>y)swap(x,y);x++; int k=lgl[y-x+1]; return min(st[k][x],st[k][y-bin[k]+1]); } int ask(int a,int x,int b,int y){ if(x>0)a=L[a]+x-1; else a=R[a]+len[a]+x; if(y>0)b=L[b]+y-1; else b=R[b]+len[b]+y; return ST_ask(a,b); } int S,T,TMP; LL dis[maxn]; int deg[maxn],q[maxn]; bool vis[maxn],in[maxn]; bool check(int u){ vis[u]=in[u]=true; for(int i=h[u];i;i=G[i].next){ int v=G[i].to; deg[v]++; if(in[v]||(!vis[v]&&check(v)))return true; }return in[u]=0; } int Get_id(int x,int y){ if(x==0)return S; if(x==-1)return T; if(y==0||y>len[x])return TMP; if(y>0)return Slen[x-1]*2+y; return Slen[x-1]*2+len[x]-y; } void add(int a,int x,int b,int y,int w){ a=Get_id(a,x);b=Get_id(b,y); add_edge(a,b,w); } char str[maxn]; int main(){ bin[0]=1; for(int i=1;i<20;++i)bin[i]=bin[i-1]<<1; lgl[0]=-1; for(int i=1;i<maxn;++i)lgl[i]=lgl[i>>1]+1; blo=26; scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%s",str); len[i]=strlen(str); Slen[i]=Slen[i-1]+len[i]; s[m++]=++blo;L[i]=m; for(int j=0;j<len[i];++j)s[m++]=str[j]-'a'+1; s[m++]=++blo;R[i]=m; for(int j=0;j<len[i];++j)s[m++]=str[len[i]-j-1]-'a'+1; } Get_sa(s,m+1,300); for(int i=0;i<=m;++i)rk[sa[i]]=i; Get_height(s,m); Get_ST(m); S=0;T=Slen[n]*2+10;TMP=T+1; bool flag=true; for(int i=1;i<=n;++i){ if(ask(i,1,i,-(len[i]+1))==len[i]){ flag=false; } } LL ans=0; for(int i=1;flag&&i<=n;++i){ for(int j=1;j<=len[i];++j){ int R=ask(i,j,i,-j),L=min(j,len[i]-j+1); ans=max(ans,1LL*R*2-1); if(L==R){ if(j>len[i]-j+1)add(0,0,i,-(j-R),R*2-1); else add(0,0,i,j+R,2*R-1); } } for(int j=2;j<=len[i];++j){ int R=ask(i,j,i,-(j-1)),L=min(j-1,len[i]-j+1); ans=max(ans,1LL*R*2); if(L==R){ if(j-1>len[i]-j+1)add(0,0,i,-(j-1-R),R*2); else add(0,0,i,j+R,R*2); } } add(0,0,i,1,0); add(0,0,i,-len[i],0); for(int j=1;j<=len[i];++j){ int wf=0,wr=0; for(int k=1;k<=n;++k){ int R=ask(i,j,k,-len[k]); if(R==len[i]-j+1)add(i,j,k,-(len[k]-R),R*2); else if(R==len[k])add(i,j,i,j+R,R*2); else wf=max(wf,R*2); R=ask(i,-j,k,1); if(R==j)add(i,-j,k,R+1,R*2); else if(R==len[k])add(i,-j,i,-(j-R),R*2); else wr=max(wr,R*2); } if(wf>0)add(i,j,-1,0,wf); if(wr>0)add(i,-j,-1,0,wr); } } if(!flag||check(S)||vis[TMP])ans=-1; else{ int he=0,t=0; q[t++]=S; while(he!=t){ int u=q[he++]; for(int i=h[u];i;i=G[i].next){ int v=G[i].to; dis[v]=max(dis[v],dis[u]+G[i].w); ans=max(ans,dis[v]); if(!--deg[v])q[t++]=v; } } } if(ans==-1)printf("Infinity\n"); else printf("%lld\n",ans); return 0; }
考试总结:
1、第三题的5分丢的太可惜,如果考试的时候在认真一点就好了
2、看题不仔细导致因为相对误差的事情浪费了大概一个半小时左右
3、最近由于题目比较简单,很容易想出正解
养成了想不出正解就不是很想写题的坏毛病,于是心态不稳,暴力就总是会出问题
从明天开始不管怎么样,都要认真对待每一道题目,哪怕是暴力也要写的优美,争取能拿到最高的分数
还有两个月啦,加油!
(两个月换三个月,貌似很值?
坑掉的东西:字符串专项训练