2020 China Collegiate Programming Contest Qinhuangdao Site(A/E/F/G/K)

题目链接:Dashboard - 2020 China Collegiate Programming Contest Qinhuangdao Site - Codeforces

目录

A. A Greeting from Qinhuangdao

E. Exam Results

F. Friendly Group 

G. Good Number

K. Kingdom's Power


A. A Greeting from Qinhuangdao

题意:有r个红球,b个蓝球,取两次,求两次都是红球的概率;

思路:答案即为c[2][r]/c[2][r+b]。

void solve() {
	int r,b,p;
	cin>>r>>b;
	int k1=r*(r-1),k2=(b+r)*(b+r-1);
	p=__gcd(k1,k2),k1/=p,k2/=p;
	cout<<"Case #"<<++t<<": "<<k1<<"/"<<k2<<endl;
}

E. Exam Results

题意:每个人的成绩有两种可能(ai或bi),设这次考试最高成绩为x,定义及格线为百分之x*p,问最多可以有多少人及格

思路:尺取,先将所有人的成绩都存入数组排个序,然后尺取合法的区间,换成暴力的思想来说,就是枚举最高分数,具体实现见代码注释。

struct ss {
	int x,y;
} a[3*maxn];
int st[3*maxn];
bool cmp(ss a,ss b) {
	return a.x<b.x;
}
void solve() {
	map<int,bool>mp;
	int n,k;
	cin>>n>>k;
	FOR(1,n) {
		cin>>a[i].x>>a[n+i].x;//a[i].x表示成绩,a[i].y表示这是第i个人的成绩 
		a[i].y=i,a[n+i].y=i;
		st[i]=st[n+i]=0;
	}
	sort(a+1,a+2*n+1,cmp);
	int l=1,r=0,now=0;
	int ans=-inf,f=0;
	while(r<2*n) {
		r++;
		if(st[a[r].y]==0)now++;//若还没选过这个人的成绩,则区间内的人数数量加一 
		st[a[r].y]++;
		if(now==n)f=1;//至少先选满n个人,再开始尺取 
		while(f&&a[l].x*100<a[r].x*k){
			if(st[a[l].y]==1)now--;//将成绩不合法的端点去掉,使得区间内只留下大于等于a[r]*p%的成绩
			st[a[l].y]--; 
			l++;
		}
		if(f)ans=max(ans,now); 
	}
	cout<<"Case #"<<++t<<": "<<ans<<endl;
}

F. Friendly Group 

题意:给定n个学生,其中有m对好朋友(x,y),老师要挑选一些学生去比赛,设定这群学生友谊值初始为0,如果一对好朋友都被挑选到,群体友谊值+1,如果一对好朋友当中只有其中一人被选中,群体友谊值−1,如果有k个学生参加了比赛,群友谊值−k,问挑选出学生群最大友谊值是多少。

思路:可以发现,如果选了一个人,这个人对于答案的贡献为-1,若选了这个人的朋友, 这个人对于答案的还是贡献为-1,并不会改变,但是若这个人的朋友与朋友之间是朋友,那么答案就会增加。所以对于一个人,要么不选他,并且也不选他的朋友,要么选他,并且将他的朋友也选择了,对于这个问题我们可以利用并查集,记录一个连通块里的朋友对数与朋友人数,若朋友对数大于了朋友人数,我们就选它。

int find(int x){
	if(x==pre[x])return x;
	return pre[x]=find(pre[x]);
}
void solve() {
	int n,m,ans=0,x,y;
	cin>>n>>m;
	FOR(1,n)pre[i]=i,sum[i]=0,num[i]=1;//sum存朋友对,num存人数 
	while(m--){
		cin>>x>>y;
		int fx=find(x),fy=find(y);
		if(fx!=fy){
			sum[fx]+=sum[fy]+1;
			num[fx]+=num[fy];
			pre[fy]=fx;
		}
		else sum[fx]++;//若祖先相同则这个连通块的朋友对数加一
	}
	FOR(1,n) if(find(i)==i)ans+=max(0ll,sum[i]-num[i]);//答案加上朋友对大于人数的数量
	cout<<"Case #"<<++t<<": "<<ans<<endl;
}

G. Good Number

题意:如果一个数字是Good Number,当且仅当x开k次方后,能整除x。即x能整除以(x开k次方后的数)现在给出n和k,求1到n之中Good Number 的个数。

思路:若一个数开k次方后等于x,则该数的范围为[x^k,x^(k+1)-1],则这个区间内只要是x的倍数的数都可以,又因为[1,x^k-1]中x的倍数的个数为⌊(x^k-1)/x⌋,[1,x^(k+1)-1]中x的倍数的个数为⌊x^(k+1)-1/x⌋,所以每个区间对于答案的贡献为⌊x^(k+1)-1/x⌋-⌊(x^k-1)/x⌋。

int qkp(int a,int b) {
	int sum=1;
	while(b) {
		if(b&1)sum*=a;
		b>>=1;
		a*=a;
	}
	return sum;
}
int t=0;
void solve() {
	int n,k,ans=0;
	cin>>n>>k;
	if(k>30||k==1) {
		cout<<"Case #"<<++t<<": "<<n<<endl;
		return;
	}
	for(int i=1;qkp(i,k)<=n;i++){
		int l=qkp(i,k),r=min(n,qkp(i+1,k)-1);
		int sum=r/i-(l-1)/i;
		ans+=sum;
	}
	cout<<"Case #"<<++t<<": "<<ans<<endl;
}

K. Kingdom's Power

题意:根节点1上有无数个军队,每一次国王都可以指挥一支军队向相邻一个节点运动,问多少次占领所有的节点。

思路:考虑行进的方案,我们必然是先深度最浅的子节点,而对于深度更深的子节点,我们只有两种遍历方案:

1.从根节点直接跑过来

2.从旁边的叶子节点绕过来

最后我们只需先跑一遍dfs,根据深度排序下叶子节点,然后再跑一遍dfs根据上述方案两者取min即可。

void dfs(int x) {
	for(auto k:e[x]) {
		dep[k]=dep[x]+1;//dep存深度
		dfs(k);
		maxx[x]=max(maxx[x],maxx[k]+1);//maxx存子节点的最深深度
	}
}
bool cmp(int x,int y) {
	return maxx[x]<maxx[y];
}
void dfs2(int x) {
	pre[x]=x;
	for(auto k:e[x]) {
		ans+=min(dep[k],dep[pre[x]]-dep[x]+1);
		dfs2(k);
		pre[x]=pre[k];
	}
}
void solve() {
	ans=0;
	cin>>n;
	FOR(0,n)dep[i]=maxx[i]=pre[i]=0,e[i].clear();
	FOR(2,n)cin>>x,e[x].push_back(i);
	dfs(1);
	FOR(1,n)sort(e[i].begin(),e[i].end(),cmp);
	dfs2(1);
	cout<<"Case #"<<++t<<": "<<ans<<endl;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值