N校联考 D2

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

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=65536
void 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 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值