• 2014-10-5 NOIP模拟赛


    祖孙询问

    (tree.pas/c/cpp)

    【问题描述】

        已知一棵n个节点的有根树。有m个询问。每个询问给出了一对节点的编号x和y,询问x与y的祖孙关系。

    【输入格式】

        输入第一行包括一个整数n表示节点个数。

        接下来n行每行一对整数对a和b表示a和b之间有连边。如果b是-1,那么a就是树的根。

        第n+2行是一个整数m表示询问个数。

        接下来m行,每行两个正整数x和y。

    【输出格式】

        对于每一个询问,输出1:如果x是y的祖先,输出2:如果y是x的祖先,否则输出0。

    【样例输入】

    10

    234 -1

    12 234

    13 234

    14 234

    15 234

    16 234

    17 234

    18 234

    19 234

    233 19

    5

    234 233

    233 12

    233 13

    233 15

    233 19

    【样例输出】

    1

    0

    0

    0

    2

    【数据规模】

        对于30%的数据,n,m≤1000。

        对于100%的.据,n,m≤40000,每个节点的编号都不超过40000。

    /*
        树剖求lca,看x和y的lca是x还是y还是其他点
    */
    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define maxn 40010
    int n,m,fa[maxn],num,head[maxn],root,sz[maxn],son[maxn],top[maxn],dep[maxn];
    struct node{
        int to,pre;
    }e[maxn*2];
    void Insert(int from,int to){
        e[++num].to=to;
        e[num].pre=head[from];
        head[from]=num;
    }
    void dfs1(int father,int now){
        fa[now]=father;
        dep[now]=dep[father]+1;
        sz[now]=1;
        for(int i=head[now];i;i=e[i].pre){
            int to=e[i].to;
            if(to==father)continue;
            dfs1(now,to);
            sz[now]+=sz[to];
            if(sz[to]>sz[son[now]]||!son[now])son[now]=to;
        }
    }
    void dfs2(int father,int now){
        top[now]=father;
        if(son[now])dfs2(father,son[now]);
        for(int i=head[now];i;i=e[i].pre){
            int to=e[i].to;
            if(to==fa[now]||to==son[now])continue;
            dfs2(to,to);
        }
    }
    int lca(int a,int b){
        while(top[a]!=top[b]){
            if(dep[top[a]]<dep[top[b]])swap(a,b);
            a=fa[top[a]];
        }
        if(dep[a]>dep[b])swap(a,b);
        return a;
    }
    int main(){ 
        //freopen("Cola.txt","r",stdin);
        freopen("tree.in","r",stdin);
        freopen("tree.out","w",stdout);
        scanf("%d",&n);
        int x,y;
        for(int i=1;i<=n;i++){
            scanf("%d%d",&x,&y);
            if(y==-1)root=x;
            else{
                Insert(x,y);
                Insert(y,x);
            }
        }
        dfs1(0,root);
        dfs2(root,root);
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            int k=lca(x,y);
            if(k==x){printf("1
    ");continue;}
            if(k==y){printf("2
    ");continue;}
            printf("0
    ");
        }
        return 0;
    }
    100分 lca

    比赛

     (mat.pas/c/cpp)

    【问题描述】

        有两个队伍A和B,每个队伍都有n个人。这两支队伍之间进行n场1对1比赛,每一场都是由A中的一个选手与B中的一个选手对抗。同一个人不会参加多场比赛,每个人的对手都是随机而等概率的。例如A队有A1和A2两个人,B队有B1和B2两个人,那么(A1 vs B1,A2 vs B2)和(A1 vs B2,A2 vs B1)的概率都是均等的50%。

        每个选手都有一个非负的实力值。如果实力值为X和Y的选手对抗,那么实力值较强的选手所在的队伍将会获得(X-Y)^2的得分。

        求A的得分减B的得分的期望值。

    【输入格式】

        第一行一个数n表示两队的人数为n。

        第二行n个数,第i个数A[i]表示队伍A的第i个人的实力值。

        第三行n个数,第i个数B[i]表示队伍B的第i个人的实力值。

    【输出格式】

        输出仅包含一个实数表示A期望赢B多少分。答案保留到小数点后一位(注意精度)

    【样例输入】

        2

        3 7

        1 5

    【样例输出】

        20.0

    【数据规模】

        对于30%的数据,n≤50。

        对于100%的.据,n≤50000;A[i],B[i]≤50000。

    /*
        排序后只需枚举一个人i,用一个指针指着另一  队中实力比i弱的里面最强的人,维护实力值的前缀和,实力值平方的前缀和即可算出期望。
        显然指针只可能向右移动,所以这一步是线性的。
        我直接用了个二分
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #define ll long long
    using namespace std;
    inline ll read(){
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    int n;
    ll a[50005],b[50005];
    ll s1[50005],s2[50005];
    ll ans1,ans2;
    int find(int x){
        int l=1,r=n,ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(b[mid]<=x)l=mid+1,ans=mid;
            else r=mid-1;
        }
        return ans;
    }
    int main(){
        freopen("mat.in","r",stdin);
        freopen("mat.out","w",stdout);
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        for(int i=1;i<=n;i++)
            b[i]=read();
        sort(b+1,b+n+1);
        for(int i=1;i<=n;i++){
            s1[i]=s1[i-1]+b[i];
            s2[i]=s2[i-1]+b[i]*b[i];
        }
        for(int i=1;i<=n;i++){
            int t=find(a[i]);
            ans1+=t*a[i]*a[i]+s2[t]-2*s1[t]*a[i];
            ans2+=(n-t)*a[i]*a[i]+(s2[n]-s2[t])-2*(s1[n]-s1[t])*a[i];
        }
        printf("%.1lf",(double)(ans1-ans2)/n);
        return 0;
    }
    100分 排序+前缀和

    数字

    (num.c/cpp/pas)

    【问题描述】

        一个数字被称为好数字当他满足下列条件:

        1. 它有2*n个数位,n是正整数(允许有前导0)

        2. 构成它的每个数字都在给定的数字集合S中。

        3. 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等

        例如对于n=2,S={1,2},合法的好数字有1111,1122,1212,1221,2112,2121,2211,2222这样8种。

        已知n,求合法的好数字的个数mod 999983。

    【输入格式】

        第一行一个数n。

        接下来一个长度不超过10的字符串,表示给定的数字集合。

    【输出格式】

        一行一个数字表示合法的好数字的个数mod 999983。

    【样例输入】

        2

        0987654321

    【样例输出】

        1240

    【数据规模】

        对于20%的数据,n≤7。

        对于100%的.据,n≤1000,|S|≤10。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    #define maxn 2010
    #define mod 999983
    int n,a[maxn],num[maxn],len;
    char ch[21];
    int dfs(int pos,int v){
        if(pos==n*2+1){
            int w=0,x=0,y=0,z=0;
            for(int i=1;i<=2*n;i++){
                if(i&1)w+=num[i];//奇数位 
                else x+=num[i];
                if(i<=n)y+=num[i];
                else z+=num[i]; 
            }
            if(w==x||y==z)return 1;
            else return 0;
        }
        int ans=0;
        for(int i=1;i<=len;i++){
            num[pos]=a[i];
            ans=(ans+dfs(pos+1,a[i]))%mod;
        }
        return ans;
    }
    int main(){
        //freopen("Cola.txt","r",stdin);
        freopen("num.in","r",stdin);
        freopen("num.out","w",stdout);
        scanf("%d%s",&n,ch+1);
        len=strlen(ch+1);
        for(int i=1;i<=len;i++)a[i]=ch[i]-'0';
        cout<<dfs(1,0);
    }
    20分 暴力
    /*
        ANS=前n位之和与后n位之和相等的方案数+奇数位之和与偶数位之和相等的方案数-前n位之和与后n位之和相等且奇数位之和与偶数位之和相等的方案数
        前2个需要+的方案数直接递推,重点是最后一个要满足2个条件的方案数怎么求:
        因为前n位之和=后n位之和,奇数位之和=偶数位之和
        所以前n位中奇数位之和=后n位中偶数位之和 且
        前n位中偶数位之和=后n位中奇数位之和
        现在只要求上面这个问题的方案数
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #define mod 999983
    #define ll long long
    using namespace std;
    inline ll read()
    {
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    int n;
    char ch[15];
    int a[15];
    int f[1005][9005];
    ll ans;
    ll cal(int x)
    {
        ll ans=0;
        for(int i=0;i<=x*9;i++)
            if(f[x][i])
                ans=(ans+((ll)f[x][i]*f[x][i]))%mod;
        return ans;
    }
    int main()
    {
        freopen("num.in","r",stdin);
        freopen("num.out","w",stdout);
        n=read();
        scanf("%s",ch);
        int l=strlen(ch);
        for(int i=0;i<l;i++)
            a[i+1]=ch[i]-'0';
        f[0][0]=1;
        for(int i=0;i<n;i++)
            for(int j=0;j<=n*9;j++)
                if(f[i][j])
                    for(int k=1;k<=l;k++)
                        f[i+1][j+a[k]]=(f[i+1][j+a[k]]+f[i][j])%mod;
        ans=2*cal(n)-cal(n/2)*cal(n-n/2);
        printf("%d",(ans%mod+mod)%mod);
        return 0;
    }
    100分
  • 相关阅读:
    vue 多层级嵌套组件传值 provide 和 inject
    vue 消息订阅与发布 实现任意组件间的通信
    成功
    疯掉的拼接
    解析发送
    一条条发
    com发送
    字符串拼接
    COM
    笨方法的combox级联
  • 原文地址:https://www.cnblogs.com/thmyl/p/7433766.html
Copyright © 2020-2023  润新知