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\n");continue;}
        if(k==y){printf("2\n");continue;}
        printf("0\n");
    }
    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分

 

posted @ 2017-08-26 07:28  Echo宝贝儿  阅读(326)  评论(0编辑  收藏  举报