此题为找规律。期望100 实际100
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; long long len1,len2; long long n,s1,t1,s2,t2; long long abss(long long x){ if(x<0) return -x; else return x; } long long maxx(long long x,long long y){ if(x>y) return x; else return y; } int main(){ freopen("grid.in","r",stdin); freopen("grid.out","w",stdout); cin>>n>>s1>>t1>>s2>>t2; len1=abss(s1-s2);len2=abss(t1-t2); cout<<maxx(len1,len2)<<endl; }
期望20~40 实际20 感觉第二部分的部分分可以枚举所有可行的字符串,然后判断是否可行,但是炸掉了。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int T,type,n,tot,num; char s[50010]; string ans[130000]; void dfs(int be,string sbef){ string smid; for(int i=be;i>=3;i--){ smid+=s[i]; int lenn=smid.length(); if(type==0){ if(smid!=sbef) if(smid.length()==2||smid.length()==3){ ans[++num]=smid; dfs(i-1,smid); } } else{ if(smid.length()==2||smid.length()==3){ ans[++num]=smid; dfs(i-1,smid); } } if(smid.length()>3) break; } } int main(){ freopen("ling.in","r",stdin); freopen("ling.out","w",stdout); scanf("%d%d",&T,&type); while(T--){ cin>>s; int len=strlen(s); if(len<=20){ dfs(len-1,"abcd"); for(int i=1;i<=num;i++){ int lenn=ans[i].length(); string simm=ans[i]; for(int j=0;j<lenn;j++) ans[i][j]=simm[lenn-j-1]; } sort(ans+1,ans+1+num); for(int i=1;i<=num;i++) if(ans[i]!=ans[i-1]) tot++; cout<<tot<<endl; for(int i=1;i<=num;i++) if(ans[i]!=ans[i-1]) cout<<ans[i]<<endl; num=0;tot=0; } else if(type==1){ for(int i=len-1;i>=3;i-=3){ string smid; for(int j=i;j>=3;j--){ smid+=s[j]; if(i-j==2||i-j==1) ans[++num]=smid; if(i-j>2) break; } } for(int i=len-1;i>=3;i-=2){ string smid; for(int j=i;j>=3;j--){ smid+=s[j]; if(i-j==2||i-j==1) ans[++num]=smid; if(i-j>2) break; } } for(int i=1;i<=num;i++){ int lenn=ans[i].length(); string simm=ans[i]; for(int j=0;j<lenn;j++) ans[i][j]=simm[lenn-j-1]; } sort(ans+1,ans+1+num); for(int i=1;i<=num;i++) if(ans[i]!=ans[i-1]) tot++; cout<<tot<<endl; for(int i=1;i<=num;i++) if(ans[i]!=ans[i-1]) cout<<ans[i]<<endl; num=0;tot=0; } } }
我们只要知道上次填的串多长,就可以知道上次使用的字符串。而且
对每个位置只需要知道它上次填2或3时是否可行。
f[i][2或3]表示当前到i,填以i开头、长度为2或3的后缀串是否可行。可以转移就记录答案。
如果typt!=0则不需要判后缀是否相同。
复杂度O(n) 。
为了方便可以把串反过来。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define pc putchar using namespace std; const int N=50005; bool f[N][2],ok2[28][28],ok3[28][28][28]; char s[N]; void Work(const int type){ memset(f,0,sizeof f); memset(ok2,0,sizeof ok2), memset(ok3,0,sizeof ok3); scanf("%s",s+1); int n=strlen(s+1); std::reverse(s+1,s+1+n); int tot=0; n-=3; if(n>=2) f[2][0]=1, ++tot, ok2[s[2]-'a'][s[1]-'a']=1; if(n>=3) f[3][1]=1, ++tot, ok3[s[3]-'a'][s[2]-'a'][s[1]-'a']=1; for(int i=4; i<=n; ++i){ int a=s[i]-'a', b=s[i-1]-'a', c=s[i-2]-'a'; if(f[i-2][1]||(f[i-2][0]&&(type||s[i]!=s[i-2]||s[i-1]!=s[i-3]))){ f[i][0]=1; if(!ok2[a][b]) ++tot, ok2[a][b]=1; } if(f[i-3][0]||(f[i-3][1]&&(type||s[i]!=s[i-3]||s[i-1]!=s[i-4]||s[i-2]!=s[i-5]))){ f[i][1]=1; if(!ok3[a][b][c]) ++tot, ok3[a][b][c]=1; } } printf("%d ",tot); for(int i=0; i<27&&tot; ++i){ for(int j=0; j<27; ++j){ if(ok2[i][j]) --tot,pc(i+'a'),pc(j+'a'),pc(' '); for(int k=0; k<27; ++k) if(ok3[i][j][k]) --tot,pc(i+'a'),pc(j+'a'),pc(k+'a'),pc(' '); } } } int main(){ int T,type; for(scanf("%d%d",&T,&type); T--; Work(type)); return 0; }
期望10 实际10
也就只会写他是条链的暴力了。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int T,q,num=1,tot,deepmax; long double ans; int son[500010],deep[500010]; int to[500010],net[500010],head[500010]; void add(int u,int v){ to[++tot]=v;net[tot]=head[u];head[u]=tot; } long double pow(long double x,int num){ long double bns=x; for(int i=1;i<num;i++) bns*=x; return bns; } void work(int now,int de){ for(int i=head[now];i;i=net[i]){ deep[de]+=son[to[i]]; deepmax=(deepmax,de); work(to[i],de+1); } } int main(){ freopen("threebody.in","r",stdin); freopen("threebody.out","w",stdout); scanf("%d%d",&T,&q); if(q>2000&&q<=3000){ for(int i=1;i<=q;i++){ int x,y; scanf("%d%d",&x,&y); if(x==1) num++; else if(x==2){ for(int j=0;j<num-y;j++) ans+=pow(0.5,j+1)*j; ans+=pow(0.5,num-y)*(num-y); printf("%.10lf ",double(ans)); } ans=0; } } else{ for(int i=1;i<=q;i++){ int x,y; scanf("%d%d",&x,&y); if(x==1){ num++;son[y]++; add(y,num); } else if(x==2){ deep[1]=son[y]; work(y,2); /*for(int j=1;j<=deepmax;j++) cout<<deep[i]<<" ";cout<<endl;*/ for(int j=1;j<=deepmax;j++){ long double bns=1.0; for(int k=1;k<=j;k++) bns=bns*(1-pow(0.5,deep[k])); bns=bns*pow(0.5,deep[j+1]); ans+=bns*j; } printf("%.10lf ",double(ans)); ans=0; } } } } /* 0 6 2 1 1 1 1 2 1 3 1 4 2 1 */
std
首先我们不需要考虑很大的深度。假如换h=100 ,需要一条长100 的链,即至少同时存在100 条
边,概率为,非常小。设需考虑的最大高度。
对于询问,只要x 有一个子树的深度为h 且其它子树深度不超过 h,就可以用p(h)*h 更新答案。
所以记f[x ][h]表示以 x为根,deep[x]<=h 的概率。则答案为
考虑如何从 转移。每个子节点有两种情况,一是存在边,对f[x][h] 贡献 1/2*f[v][h-1]的概率;二是不存在该边,概率为1/2 。
类似多项式,把 项乘在一起,即
(比如,深度为0,对应 深度为1,对应 个 1/2与一个……)
复杂度 。
如果新建节点 ,则影响的点有
因为深度不超过70 ,所以暴力向上更新即可,也就是除掉之前的项,乘上新的项。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAX_H 60 #define MAXN 500010 using namespace std; int T,Q,n,fa[MAXN]; double f[MAXN][MAX_H]; int main(){ n=1;scanf("%d%d",&T,&Q); for(int i=0;i<MAX_H;i++) f[1][i]=1; while(Q--){ int opt,x; scanf("%d%d",&opt,&x); if(opt==1){ fa[++n]=x; for(int i=0;i<MAX_H;i++) f[n][i]=1; double tmp1=f[x][0],tmp2; f[x][0]*=0.5; for(int Fa=fa[x],i=1;Fa&&i<MAX_H;Fa=fa[x=Fa],i++){ tmp2=f[Fa][i]; f[Fa][i]/=0.5+0.5*tmp1; f[Fa][i]*=0.5+0.5*f[x][i-1]; tmp1=tmp2; } } else { double ans=0; for(int i=1;i<MAX_H;i++) ans+=(f[x][i]-f[x][i-1])*i; printf("%.10lf ",ans); } } }