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

目录

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<<": "<

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.xmp;
	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

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<<": "<

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<<": "<

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]>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<<": "<

你可能感兴趣的:(算法,c++)