18.9.22 noip模拟赛

此题为找规律。期望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;
        }
    }
}
View Code

我们只要知道上次填的串多长,就可以知道上次使用的字符串。而且
对每个位置只需要知道它上次填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\n",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('\n');
            for(int k=0; k<27; ++k)
                if(ok3[i][j][k]) --tot,pc(i+'a'),pc(j+'a'),pc(k+'a'),pc('\n');
        }
    }
}
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\n",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\n",double(ans));
                ans=0;
            }
        }
    }
}
/*
0 6
2 1
1 1
1 2
1 3
1 4
2 1
*/
View Code

 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\n",ans);
        }
    }
}

 

posted @ 2018-09-22 14:32  一蓑烟雨任生平  阅读(140)  评论(0编辑  收藏  举报