[考试反思]0117省选模拟10:争夺
T3出了一点锅,于是按IOI赛制打的。
可能也是这辈子唯一一次好好打的IOI赛制了。
提答,又沉里面了,进去就出不来。莫名的虚荣让我根本没有回头看传统题。
于是的确在T3的80%时间里一直单题rk1,然而其实很慌,剩下两道题又怎样?
运气好,T1特别水,T2数据水,T3用奇技淫巧多拿7分于是并列rk1了。
数组没清空丢了1分。。。
状态差的不行,一下午一道题都没改出来。。。咕到第二天也没改出来
于是在第二天考试的时候写完三个暴力之后终于把T2A了。。。
然后第二天考试就炸了,没什么好说的
T1:食物链
大意:就生物上那个。数条数。$n \le 100000,m\le 200000$
当成生物题做就行。拓扑。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 200005 4 int in[S],out[S],deg[S],l[S],fir[S],to[S],ec,n,m,q[S],t;long long dp[S],ans; 5 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;in[b]++;out[a]++;} 6 int main(){ 7 scanf("%d%d",&n,&m); 8 for(int i=1,a,b;i<=m;++i)scanf("%d%d",&a,&b),link(a,b); 9 for(int i=1;i<=n;++i)if(!in[i])q[++t]=i,dp[i]=1;else deg[i]=in[i]; 10 for(int h=1;h<=t;++h)for(int i=fir[q[h]];i;i=l[i]){ 11 dp[to[i]]+=dp[q[h]];deg[to[i]]--; 12 if(!deg[to[i]])q[++t]=to[i]; 13 } 14 for(int i=1;i<=n;++i)if(in[i]&&!out[i])ans+=dp[i]; 15 cout<<ans<<endl; 16 }
T2:选点游戏
大意:支持连边,询问某个联通块中最大独立集。点权为1。保证最后是个树。$n\le 200000,m\le 800000$
维护子树信息的$LCT$。每个$splay$根维护的是一条实链,开四个变量维护这条链的顶端和底端分别选或不选的最优值。
比$ddp$和正解都要好理解,好写一些。。。但是常数巨大。
有一点小的分类讨论,其实代码非常好写。。。$1.6k$
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int S=200005,inf=20040124; 4 int c[2][S],f[S],s[S],lz[S],dp0[S],dp1[S]; 5 #define lc c[0][p] 6 #define rc c[1][p] 7 int Max(int a,int b,int c){return max(max(a,b),c);} 8 struct V{ 9 int __,_$,$_,$$; 10 V(int a=0,int b=0,int c=0,int d=0){__=a,_$=b;$_=c;$$=d;} 11 int val(){return Max(max(__,$_),_$,$$);} 12 V operator+(V x){return V(Max(__+x.__,__+x.$_,_$+x.__),Max(__+x._$,__+x.$$,_$+x._$),Max($_+x.__,$_+x.$_,$$+x.__),Max($_+x._$,$$+x._$,$_+x.$$));} 13 }v[S]; 14 bool nr(int p){return c[0][f[p]]==p||c[1][f[p]]==p;} 15 void rev(int p){lz[p]^=1,swap(lc,rc),swap(v[p].$_,v[p]._$);} 16 void up(int p){v[p]=V(dp0[p],-inf,-inf,dp1[p]+1);if(lc)v[p]=v[lc]+v[p];if(rc)v[p]=v[p]+v[rc];} 17 void down(int p){if(lz[p])rev(lc),rev(rc),lz[p]=0;} 18 void spin(int p){ 19 int F=f[p],G=f[F],D=c[1][F]==p,B=c[!D][p]; 20 if(nr(F))c[c[1][G]==F][G]=p; c[!D][p]=F; c[D][F]=B; 21 if(B)f[B]=F; f[f[F]=p]=G; up(F); 22 } 23 void splay(int p){ 24 int tp=1,y=p,F;s[1]=p; 25 while(nr(y))s[++tp]=y=f[y]; while(tp)down(s[tp--]); 26 for(;F=f[p],nr(p);spin(p))if(nr(F))spin(c[1][F]==p^c[1][f[F]]==F?p:F); up(p); 27 } 28 void asas(int r){ 29 for(int y=0,p=r;p;p=f[y=p]){ 30 splay(p); 31 dp0[p]+=v[rc].val()-v[y].val(); 32 dp1[p]+=max(v[rc].__,v[rc]._$)-max(v[y].__,v[y]._$); 33 rc=y,up(p); 34 }splay(r); 35 } 36 void make(int p){asas(p);rev(p);} 37 void link(int x,int y){make(x);make(y);f[x]=y;dp0[y]+=v[x].val();dp1[y]+=max(v[x].__,v[x]._$);} 38 int main(){//freopen("1.in","r",stdin); 39 int n,m;cin>>n>>m;while(m-->0){ 40 int opt,x,y;scanf("%d",&opt); 41 if(opt)scanf("%d%d",&x,&y),link(x,y); 42 else scanf("%d",&x),make(x),printf("%d\n",v[x].val()); 43 } 44 }
T3:随机
大意:看代码求输出的期望。
$Test1:$随机返回$[1,n]$的随机数。
$\frac{1+n}{2}$
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int data[]={1,10,100,1024,10000,20000,50000,100000,524288,1000000}; 4 int main(){ 5 freopen("random1.out","w",stdout); 6 for(int i=0;i<10;++i)printf("%.10Lf\n",(1.0L+data[i])/2); 7 }
$Test2:$$n$个$[1,1000000]$的随机变量的最小值。
《地震后的幻想乡》的结论,$n$个随机$[0,1]$变量的最小值期望是$\frac{1}{n+1}$
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int data[]={1,10,100,1024,10000,20000,50000,100000,524288,1000000}; 4 int main(){ 5 freopen("random2.out","w",stdout); 6 for(int i=0;i<10;++i){ 7 __float128 ans=0; 8 for(int x=1;x<=1000000;++x){ 9 ans+=pow(x/1000000.0L,data[i]); 10 }printf("%.10Lf\n",(long double)ans); 11 } 12 }
$Test3:$不断在$n$个变量中随机取值,求所有变量都被取过的期望步数。
求取到下一个没取过的变量的期望步数进行累加。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int data[]={1,10,100,1024,10000,20000,50000,100000,524288,1000000}; 4 int sta[11111]; 5 int main(){ 6 freopen("random3.out","w",stdout); 7 for(int i=0;i<10;++i){ 8 __float128 ans=0; 9 for(int j=1;j<=data[i];++j)ans+=1.0L/j; 10 printf("%.10Lf\n",(long double)(ans*data[i])); 11 } 12 }
$Test4:$不断在$n$个变量中随机取值,求某一个变量被取过两次的期望步数。
枚举步数,计算概率进行累加,概率很好算是个前缀积。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int data[]={1,10,100,1024,10000,20000,50000,100000,524288,1000000}; 4 int main(){ 5 freopen("random4.out","w",stdout); 6 for(int i=0;i<=9;++i){ 7 __float128 pre=1,ans=0; 8 for(int t=1;t<=data[i];++t){ 9 ans+=pre*t/data[i]*(t+1); 10 pre*=((__float128)data[i]-t+0.0L)/data[i]; 11 }printf("%.10Lf\n",(long double)ans); 12 } 13 }
$Test5:$求线段树区间询问时期望涉及多少个节点。
考虑每个节点会在多少个方案中出现,当且仅当询问区间与当前节点区间相交且不包含父节点的完整区间。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int data[]={1,10,100,1024,10000,20000,50000,100000,524288,1000000}; 4 __float128 ans,all;int I; 5 void build(int l,int r,int fl,int fr){ 6 int lp=l-1,rp=data[I]-r; 7 ans+=all-(lp+1.0L)*lp/2-(rp+1.0L)*rp/2-1.0L*fl*(data[I]-fr+1); 8 if(l==r)return; 9 build(l,l+r>>1,l,r);build((l+r>>1)+1,r,l,r); 10 } 11 int main(){ 12 freopen("random5.out","w",stdout); 13 for(int i=0;i<=9;++i){I=i; 14 ans=0;all=(data[i]+1.0L)*data[i]/2;build(1,data[i],0,-1); 15 printf("%.10Lf\n",(long double)(ans/all)); 16 } 17 }
$Test6:$求$Treap$期望深度和。
考虑加入一个节点的贡献,设$dp[n]$为$n$个节点的树的答案,那么$dp[n]=\frac{\sum\limits_{i=1}^{n} dp[i-1]+dp[n-i]}{n}$。
上边那个就是一个前缀和。
1 #include<bits/stdc++.h> 2 using namespace std; 3 long double dp[1000005],pre[1000005]; 4 const int data[]={1,10,100,1024,10000,20000,50000,100000,524288,1000000}; 5 int main(){ 6 freopen("random6.out","w",stdout); 7 dp[1]=pre[1]=1; 8 for(int i=2;i<=data[9];++i)dp[i]=2*pre[i-1]/i+i,pre[i]=pre[i-1]+dp[i]; 9 for(int i=0;i<=9;++i)printf("%.10Lf\n",dp[data[i]]); 10 }
$Test7:$不会。
$Test8:$不断在$n$个变量中随机取值,不会取到已经取过的,取到时它的所有倍数也被取到。求所有变量都被取过的期望步数。
每个节点被取到之可能是它的某个约数被取到,概率是均等的,所以答案是$\sum\limits_{i=1}^{n} \frac{1}{d(i)}$
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int data[]={1,10,100,1024,10000,20000,50000,100000,524288,1000000}; 4 __float128 ans;int I; 5 int main(){ 6 freopen("random8.out","w",stdout); 7 for(int i=0;i<=9;++i){ 8 ans=0; 9 for(int x=1;x<=data[i];++x){ 10 int cnt=0; 11 for(int y=1;y*y<=x;++y)if(x%y==0){ 12 cnt++;if(y*y!=x)cnt++; 13 } 14 ans+=1.0L/cnt; 15 } 16 printf("%.10Lf\n",(long double)ans); 17 } 18 }
$Test9:$值域为$10$,长度为$n$的随机序列的期望$LIS$。
当$n$很大时答案直接近似成$10$。小点十进制压状态记搜$0.05s$都跑不到。
$LIS$的转移只与前面每个值对应的最大$dp$值有关,压起来就搜就行了,实际上$1000$的点都跑不到$0.5s$
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int data[]={1,10,100,1024,10000,20000,50000,100000,524288,1000000}; 4 unordered_map<int,long double>M[101]; 5 int I,a[11],dp[11];long long ans,tms; 6 int r[101][11],rr[101][11]; 7 long double sch100(int stp,int st){ 8 if(M[stp].find(st)!=M[stp].end())return M[stp][st]; 9 if(stp>6)cerr<<stp<<' '<<st<<endl; 10 if(!stp)return st%10;int rst=st; 11 for(int i=10;i;--i)r[stp][i]=st%10,st/=10; 12 long double tot=0; 13 for(int c=1;c<=10;++c){ 14 for(int i=1;i<=10;++i)rr[stp][i]=r[stp][i]; 15 rr[stp][c]=max(rr[stp][c],rr[stp][c-1]+1); 16 if(c==10&&rr[stp][10]==10){tot+=1;break;} 17 for(int s=c+1;s<=10;++s)rr[stp][s]=max(rr[stp][s],rr[stp][s-1]); 18 register int nst=0; 19 for(int i=1;i<=10;++i)nst=(nst<<3)+(nst<<1)+rr[stp][i]; 20 tot+=0.1*sch100(stp-1,nst); 21 }return M[stp][rst]=tot; 22 } 23 int main(){ 24 freopen("random9.out","w",stdout); 25 printf("%.10Lf\n",sch100(100,0)); 26 }
$Test10:$不会。