N校联考 D2

来源:互联网 发布:桌面录制视频软件 编辑:程序博客网 时间:2024/05/17 06:31

今天开头就写着几个大字
信心题
浅览题目 好像都不会诶
我肯定是打开方式不对 于是我又重新开了一遍
发现依旧不会做 于是乱搞

1.祖孙询问

【问题描述】
已知一棵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。

恩 对于树结构的东西我一般都不怎么会
一看 好的 暴力还是可以的
然后想想 其实dfs 一下处理记录处理的顺序 然后就有一些规律了

void dfs(int x){  timein[x] =cur++;  for(int i=head[x]; i!=-1 ;i=e[i].next){    if(timein[e[i].v]==0)    {      dfs(e[i].v);    }  }  timeout[x] = cur++;}for(int i=1 ;i<=m ;i++)  {    int x=read(),y=read();    if(x==-1){printf("1\n");continue;}    if(y==-1){printf("2\n");continue;}    if(timein[x] < timein[y] && timeout[x] >timeout[y]){printf("1\n");continue;}    if(timein[x] > timein[y] && timeout[x] <timeout[y]){printf("2\n");continue;}        printf("0\n");  }

以上就是我的大致思路。。。
其实我觉得应该可以过的
但没有 其实dfs 可以改一下的

当然这是很明显的LCA问题了
但我不会 或者说 记不得
然后重新看了看

#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<iostream>#include<algorithm>using namespace std;int n,m,zz,head[40002],root;struct bian{int to,nx;} e[80002];int fa[40002][20],h[40002];//2^16=65536void insert(int x,int y){    zz++; e[zz].to=y; e[zz].nx=head[x]; head[x]=zz;    zz++; e[zz].to=x; e[zz].nx=head[y]; head[y]=zz;}void init(){    int i,x,y;    scanf("%d",&n);    for(i=1;i<=n;i++)       {scanf("%d%d",&x,&y);        if(y==-1) root=x;        else insert(x,y);       }}void dfs(int x){    int i,p;    for(i=1;i<=15;i++)       {if(h[x]<(1<<i)) break;        fa[x][i]=fa[fa[x][i-1]][i-1];       }    for(i=head[x];i;i=e[i].nx)       {p=e[i].to;        if(p==fa[x][0]) continue;        h[p]=h[x]+1; fa[p][0]=x;        dfs(p);       }}int lca(int x,int y){    if(h[x]<h[y]) swap(x,y);    int i,c;    c=h[x]-h[y];    for(i=15;i>=0;i--)       {if(c&(1<<i)) x=fa[x][i];}    for(i=15;i>=0;i--)       {if(fa[x][i]!=fa[y][i])           {x=fa[x][i]; y=fa[y][i];}       }    if(x==y) return x;    else return fa[x][0];}void work(){    int i,x,y,t;    scanf("%d",&m);    dfs(root);    for(i=1;i<=m;i++)       {scanf("%d%d",&x,&y);        if(x==y) printf("0\n");        else           {t=lca(x,y);            if(t==x) printf("1\n");            else if(t==y) printf("2\n");            else printf("0\n");           }       }}int main(){  .......}

dfs 好像可以 如果担心爆就bfs吧

2.比赛

【问题描述】
有两个队伍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。


我坚信暴力一定都会
然后排序再暴力一定可以优化
然后其实排序之后拿个指针记录就可以线性完成
我们算A的所以把B给预处理了
S【】记前缀和 SQ【】记前缀平方和 (*long long 很重要)

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;inline int read(){ // int    int 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;}void init(){   freopen("mat.in","r",stdin);   freopen("mat.out","w",stdout);}int n;int a[50005],b[50005];double ans = 0.0;long long s[50005],sq[50005];inline long long sqr(long long  x){  return x*x;}void readdata(){  n=read();  for(int i=1 ;i<=n ;i++)  a[i]=read();// A power-range  for(int i=1 ;i<=n ;i++)  b[i]=read();// B power-range  //have a sort to get up-arr  sort(a+1 ,a+n+1);  sort(b+1 ,b+n+1);  for(int i=1 ;i<=n ;i++) //维护前缀   {    sq[i] = sqr(b[i]) + sq[i-1]; // b[i]^2    s[i] = b[i] + s[i-1];        // b[i]  }}// liner-work()  void work(){  int L=0;// L+1  double tem = 0.0;    for(int i=1 ;i<=n ;i++) // enumeration  {  tem = 0.0;     while(L<n && a[i] > b[L+1])//     {       L++;             }     //二次项展开合并      tem += L * sqr(a[i]) - 2 * s[L] * a[i] + sq[L];    tem -= (n - L) * sqr(a[i]) - 2 * (s[n] - s[L]) *a[i] + (sq[n] - sq[L]);     tem /= n;     ans+=tem;  }} int main(){   init();   readdata();   work();   printf("%.1lf",ans);  }

(那些无聊的注释直接无视)

3.数字

【问题描述】
一个数字被称为好数字当他满足下列条件:
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。

题解:
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>#define mod 999983#define ll long longusing 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;}
0 0
原创粉丝点击