AtCoder Beginner Contest 160(D、E、F


D - Line++

题意:

给n,x,y
表示存在一个n个顶点的无向图图,对于每一对(i,i+1)都存在边,那么整个图就是一条链
现在顶点x和顶点y之间添加了一条边,
所有边的长度都是1

现在问:
有多少点对(a,b),满足a到b的最短距离为1,输出点对数量,
有多少点对(a,b),满足a到b的最短距离为2,输出点对数量,
有多少点对(a,b),满足a到b的最短距离为3,输出点对数量,

有多少点对(a,b),满足a到b的最短距离为n-1,输出点对数量,

数据范围:n<=1e3,1<=x<y<=n

解法:

观察到n只有1e3,因此O(n2)枚举点对,记录最短距离出现的次数即可
求i到j最短距离的时候,计算不走x-y的距离,和走x-y的距离,取min就是最短距离了

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
int mark[maxm];
signed main(){
    int n,x,y;
    cin>>n>>x>>y;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            int a=abs(i-j);//按原来的路走
            int b=abs(x-i)+abs(y-j)+1;//走x-y
            int d=min(a,b);
            mark[d]++;
        }
    }
    for(int i=1;i<=n-1;i++)cout<<mark[i]<<endl;
    return 0;
}

E - Red and Green Apples

题意:

给定x,y,A,B,C
分别表示你现在要吃x个红苹果,y个绿苹果,
你现在有A个红苹果,B个苹果,C个无色苹果
然后给长度为A的数组a,a(i)表示第i个红苹果的权值
然后给长度为B的数组b,b(i)表示第i个绿苹果的权值
然后给长度为C的数组c,c(i)表示第i个无色苹果的权值

吃无色苹果之前可以把他先染色为红色或者绿色,
现在问你吃x个红苹果,y个绿苹果之后的最大权值和

数据范围:x,y,A,B,C<=1e5,a(i),b(i),c(i)<=1e9

解法:

容易想到排序之后优先取最大。
容易进入的误区是吃无色苹果之前要把它先染成某一种颜色,然后就不知道怎么处理了。

其实不用管染成哪种色,多开一个变量记录选择的无色苹果的个数
假设选择xx个红色,yy个绿色,zz个无色,当xx+yy+zz==x+y的时候退出即可。
不需要确定每个选择的无色苹果染成哪种颜色。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=3e5+5;
struct Node{
    int v;
    int id;
}e[maxm];
signed main(){
    int x,y,A,B,C;
    cin>>x>>y>>A>>B>>C;
    int n=0;
    for(int i=1;i<=A;i++)cin>>e[++n].v,e[n].id=1;
    for(int i=1;i<=B;i++)cin>>e[++n].v,e[n].id=2;
    for(int i=1;i<=C;i++)cin>>e[++n].v,e[n].id=3;
    sort(e+1,e+1+n,[](Node a,Node b){return a.v>b.v;});
    int xx=0,yy=0,zz=0;
    int ans=0;
    for(int i=1;i<=n;i++){
        if(e[i].id==1){
            if(xx<x)xx++,ans+=e[i].v;
        }else if(e[i].id==2){
            if(yy<y)yy++,ans+=e[i].v;
        }else{
            if(xx+yy+zz<x+y)zz++,ans+=e[i].v;
        }
        if(xx+yy+zz==x+y)break;
    }
    cout<<ans<<endl;
    return 0;
}

F - Distributing Integers

题意:

给一颗n个顶点的树
先选择一个点作为起始顶点,填上数字1,
然后任选一个与有数字点相邻的无数字点,填上2,
然后任选一个与有数字点相邻的无数字点,填上3,
重复操作知道填完。

现在问你1到n的每个顶点轮流作为起始点,分别有多少种填数字的可能,答案对1e9+7取模。

解法:

这道题其实就是计算以每个顶点作为根的拓扑序列数。
对于有根树,拓扑序列数为:
在这里插入图片描述

那么这题就是对于每个顶点,计算所有子树大小的乘积。

树形dp换根即可。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
const int mod=1e9+7;
vector<int>g[maxm];
int mul[maxm];
int sz[maxm];
int n;
int ppow(int a,int b,int mod){
	int ans=1%mod;a%=mod;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
void dfs1(int x,int fa){
    sz[x]=1;
    for(int v:g[x]){
        if(v==fa)continue;
        dfs1(v,x);
        sz[x]+=sz[v];
        mul[x]=mul[x]*mul[v]%mod;
    }
    mul[x]=mul[x]*sz[x]%mod;
}
void dfs2(int x,int fa){
    for(int v:g[x]){
        if(v==fa)continue;
        //mul[x]是正确的
        int pre=mul[x]*ppow(n,mod-2,mod)%mod*ppow(mul[v],mod-2,mod)%mod*(n-sz[v])%mod;
        mul[v]=mul[v]*ppow(sz[v],mod-2,mod)%mod*pre%mod*n%mod;
        dfs2(v,x);
    }
}
signed main(){
    cin>>n;
    for(int i=1;i<n;i++){
        int a,b;
        cin>>a>>b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    for(int i=1;i<=n;i++)mul[i]=1;
    dfs1(1,-1);
    dfs2(1,-1);
    int nn=1;
    for(int i=1;i<=n;i++){//阶乘
        nn=nn*i%mod;
    }
    for(int i=1;i<=n;i++){
        cout<<nn*ppow(mul[i],mod-2,mod)%mod<<endl;
    }
    return 0;
}

发布了445 篇原创文章 · 获赞 37 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/105272381