2020CCPC秦皇岛部分题解AEFGK

由于个人真的菜,所以我没打秦皇岛,只有一个威海打星名额,上星期跟着队友做志愿者,实则在隔壁房间摸鱼读题,这两天有时间就认真做了做,K题的树型dp让我现场写肯定是写不出来的,所以我还看了别人的代码后做的。至于别的题,后面陆续有时间再做了 没时间也不要喷我QAQ
现在已经有人把题目上传到Gym 102769了,有兴趣可以自己去搜一下

A.Greeting from Qinhuangdao

传送门
题意:r个红球,b个蓝球,问连续两次取出红球的概率,以最简分式形式输出
题意:特判概率为0和1, a ÷ ( a + b ) × ( a − 1 ) ÷ ( a − 1 + b ) a\div(a+b)\times(a-1)\div(a-1+b) a÷(a+b)×(a1)÷(a1+b),取出gcd(分子,分母),后化简即可

#pragma GCC optimize(2)
#include<bits/stdc++.h> 
using namespace std;
#define AC 0 
#define Please return
#define endl "\n" 
#define ll long long
int main(){
    ios_base::sync_with_stdio(0);cin.tie(0),cout.tie(0);
	int _,cas=1;cin>>_;
	while(_--){
		ll r,b;cin>>r>>b;
		cout<<"Case #"<<cas++<<": ";
		if(r<2)cout<<"0/1"; 
		else if(b==0)cout<<"1/1";
		else {
			ll fir=r*(r-1),sec=(r+b)*(r+b-1),gcd=__gcd(fir,sec);
			cout<<fir/gcd<<"/"<<sec/gcd;
		}
		cout<<endl;
	}
   Please AC;
} 

E.Exam Results

题意:n个人,每个的成绩有两种可能,分别是 a i a_i ai b i b_i bi,合格线是这n个人中成绩最高的人乘以p%,问最多能有多少个人合格。

#pragma GCC optimize(2)
#include<bits/stdc++.h> 
using namespace std;
#define AC 0 
#define Please return
#define endl "\n" 
#define ll long long
const int MAX=4e5+7;   //注意范围
struct node{
	int val,pos; 
}a[MAX];
int b[MAX],c[MAX]; 
bool cmp(node a,node b){
	return a.val==b.val?a.pos<b.pos:a.val>b.val;
}
int main(){
    ios_base::sync_with_stdio(0);cin.tie(0),cout.tie(0);
	int _,cas=1;cin>>_;
	while(_--){
		cout<<"Case #"<<cas++<<": ";
		ll n,p;cin>>n>>p; 
		for(int i=1;i<=n;i++)b[i]=c[i]=0;
		for(int i=1;i<=n;i++){
			cin>>a[2*i-1].val; a[2*i-1].pos=i;
			cin>>a[2*i].val; a[2*i].pos=i;
		}
		sort(a+1,a+2*n+1,cmp);
		int cnt=0,res=0,pos1=0,pos2=0,ans=0;
		for(int i=1;i<=n;i++){
			while((pos1+1)<=2*n&&cnt<n){
                b[a[++pos1].pos]++;//前n个人每个位置出现的次数
                if (b[a[pos1].pos]==1) cnt++; //出现一次记录
            }
            while((pos2+1)<=2*n&&a[pos2+1].val>=((1ll*a[i].val*p%100==0)?(1ll*a[i].val*p/100):(1ll*a[i].val*p/100 +1))){
                c[a[++pos2].pos]++;//及格的
                if (c[a[pos2].pos] == 1) res++; //出现一次记录
            }
            if (cnt == n) ans = max(ans, res);
            b[a[i].pos]--;//除去当前值
            if (b[a[i].pos] == 0) cnt--;
            c[a[i].pos]--;
            if (c[a[i].pos] == 0) res--;
		} 
		cout<<ans<<endl; 
	}
   Please AC;
} 

F.Friendly Group

题意:n个人中m个关系,每次可任意组合k人去参加会议(0<=k<=n),且可知友谊值=k人中存在的关系数-k。
通俗的理解就是n个点中有m条边,选出任意点组成连通块中边数-点数最大值和为多少
题解:dfs或者并查集维护连通块的点数和边数, ∑ 每 个 连 通 块 \sum_{每个连通块} 边数-点数>0的值 注意别用map记录连通块,因为容易炸

#pragma GCC optimize(2)
#include<bits/stdc++.h> 
using namespace std;
#define AC 0 
#define Please return
#define endl "\n" 
#define ll long long
const int MAX=3e5+7;  
int pre[MAX],n,m;
int nv[MAX],nl[MAX];
void init(){
	for(int i=1;i<=n;i++)pre[i]=i,nv[i]=1,nl[i]=0;
}
int Find(int x){
	return pre[x]==x?x:pre[x]=Find(pre[x]);
}
int main(){
    ios_base::sync_with_stdio(0);cin.tie(0),cout.tie(0);
	int _,cas=1;cin>>_;
	while(_--){
		 n,m;cin>>n>>m;
		init();
		cout<<"Case #"<<cas++<<": ";
		for(int i=0;i<m;i++){
			int x,y;cin>>x>>y;
			int fx=Find(x),fy=Find(y);
			if(fx!=fy){
				pre[fy]=fx;
				nv[fx]+=nv[fy]; 
				nl[fx]+=nl[fy];
				nl[fx]++;
			}
			else nl[fx]++;
		}
		ll ans=0;
		for(int i=1;i<=n;i++)
			if(nv[i]>=2)ans+=max(0,nl[i]-nv[i]);
		cout<<ans<<endl;
	}
   Please AC;
} 

G.Good Number

题意:输入一个n和k,问1-n中有多少个开了k次根后还可以整除它本身。
题解:注意题目给的数据范围是 1 1 1~ 1 0 9 10^9 109 2 30 2^{30} 230=1,073,741,824,所以保险可以见最多只能去到 2 31 2^{31} 231,k≥31后,开k次根都是得到1,1可以除以任何正整数,故有n个满足
然后从1开始暴力判断k次方且满住小于n的数,再通过每段区间的数学规律,遍历求出结果即可。

#pragma GCC optimize(2)
#include<bits/stdc++.h> 
using namespace std;
#define AC 0 
#define Please return
#define endl "\n" 
#define ll long long
ll qpow(ll a,ll b){
	ll res=1;
	for(;b;b>>=1){
		if(b&1)res=a*res;
		a*=a;
	}
	return res;
}
int main(){
    ios_base::sync_with_stdio(0);cin.tie(0),cout.tie(0);
	int _,cas=1;cin>>_;
	while(_--){
		ll n,k;cin>>n>>k;
		cout<<"Case #"<<cas++<<": ";
		if(k>=31||k==1)cout<<n;
		else {
			vector<ll>p; p.push_back(0);
			for(int i=1;i<=n;i++){
				ll x=qpow(i,k);
				if(x>n)break;
				p.push_back(x);
			}
			ll ans=0;
			for(int i=2;i<p.size();i++)
				ans+=(p[i]-1-p[i-1])/(i-1)+1;
			if(p.back()==n)ans++;
			else ans+=(n-p.back())/(p.size()-1)+1;
			cout<<ans;
		}
		cout<<endl;
	}
   Please AC;
} 

K.Kingdom’s Power

题意:王国在1节点且该点为树的根节点,每天最多出动一个军队,每个军队每天最多在相邻的边移动一次,问最少需要多少天可以遍历完所有的城市
题解:类树形dp,对于每个点都维护以该节点为根节点的子树

#pragma GCC optimize(2)
#include<bits/stdc++.h> 
using namespace std;
#define AC 0 
#define Please return
#define endl "\n" 
#define ll long long
const int MAX=2e6+7;  //注意范围
struct node{
	ll a,maxdep,ans;
}dp[MAX];
struct Edge{
	int to,next;
}edge[MAX];
int n,head[MAX],dep[MAX],cnt=0;
void add_edge(int u,int v){
	edge[++cnt]={v,head[u]};head[u]=cnt;
	edge[++cnt]={u,head[v]};head[v]=cnt;
} 
void dfs(int u,int fa){
	node mx={0,0,0};
	dp[u]={1,dep[u],0};
	int mxn=0,suma=0,son=0;
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[i].to;
		if(v==fa)continue; 
		son++; dep[v]=dep[u]+1;
		dfs(v,u);
		dp[u].maxdep=max(dp[u].maxdep,dp[v].maxdep);
		if(dp[v].maxdep>mx.maxdep)mx=dp[v],mxn=v; 
	} 
	ll sum=0;
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[i].to;
		if(v==fa||v==mxn)continue;  //排除父节点和最长链 
		sum+=dp[v].ans+dp[v].a;
		suma+=dp[v].a;
		if(dp[v].maxdep-dep[u]<dep[u]-dep[1]){
			sum+=dp[v].maxdep-dep[u]; suma--;
		}
	}
	if(son==0)return ;
	sum+=dp[mxn].a+dp[mxn].ans;
	suma+=dp[mxn].a; 
	if(dp[mxn].maxdep-dep[u]<dep[u]-dep[1]) suma--;
	if(suma==0)suma=1;
	dp[u].a=suma;  dp[u].ans=sum;
} 
int main(){
    ios_base::sync_with_stdio(0);cin.tie(0),cout.tie(0);
	int _,cas=1;cin>>_;
	while(_--){
		cin>>n;
		cnt=0;
		for(int i=0;i<=n;i++)head[i]=0;
		cout<<"Case #"<<cas++<<": ";
		for(int i=2;i<=n;i++){
			int v;cin>>v;
			add_edge(v,i);
		}
		dep[1]=0;
		dfs(1,1);
		cout<<dp[1].ans<<endl;
	}	
   Please AC;
} 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值