Codeforces 916 div3 A-F

A

题意分析:

看思考每个题的时间是否大于等于解决该题需要的时间,如果是,则能解决该题,否则不能,直接枚举计数即可

C++代码:

#include<iostream>
using namespace std;
int main(){
	int t,n;
	cin>>t;
	while(t--){
		cin>>n;
		string s;
		cin>>s;
		int cnt[26]={0};
		for(int i=0;i<s.size();i++){
			cnt[s[i]-'A']++;//思考A题的时间加一
		}
		int res=0;
		for(int i=0;i<26;i++){
			if(cnt[i]>=i+1)res++;//思考时间大于等于需要的时间
		}
		cout<<res<<endl;
	}
	return 0;
}

B

题意分析:

一开始先让数组按照n~1的顺序排列,然后将前k+1个数从小到大排序即可

C++代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int N=55;
int a[N];
bool cmp(int a,int b){
	return a>b;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		int n,k;
		cin>>n>>k;
		for(int i=1,j=n;i<=n;i++,j--)a[i]=j;//先将所有数倒序
		sort(a+1,a+k+2);//把前k+1排成正序,就有了k次兴奋次数啦
		for(int i=1;i<=n;i++)cout<<a[i]<<" ";
		cout<<endl;
	}
	return 0;
}

C

题意分析:

 要求完成不超过k个任务可获得的最大的经验值

由于b数组中的元素都大于1,所以我们一定会取到k个任务

假设i表示我们完成的任务截止到第i个,即i后面的任务都没做

此时可获得的最大经验值为a[1]+a[2]+...+a[i]+(k-i)*(b[1~i]中的最大值)

所以要先预处理a的前缀和和b的前缀最大值

然后我们只需枚举每个任务作为分界点即可

C++代码:

#include<iostream>
using namespace std;
const int N=200010;
int a[N],b[N],maxx[N];
int n,k;
void solve(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)maxx[i]=0;
	for(int i=1;i<=n;i++)cin>>a[i],a[i]+=a[i-1];//a变成其前缀和
    //maxx[i]含义:b[1~i]中最大的一个数
	for(int i=1;i<=n;i++)cin>>b[i],maxx[i]=max(maxx[i-1],b[i]);
	int ans=0;
    //从前往后枚举解决的问题截止到第几个
	for(int i=1;i<=min(n,k);i++){
		int sum=a[i]+(k-i)*maxx[i];//一共解决到第i个,剩下的k-i个直接用b[1~i]中最大的一个
		ans=max(ans,sum);//更新ans
	}
	cout<<ans<<endl;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

题意分析:

先找出每种活动人数最多的三天,即找出每一行中最大的三个数

显然,最优解一定是从这些数中每一行取一个,前提是这三个数不能在同一列,所以在存储最大的三个数时还要存一下坐标

C++代码:

#include<iostream>
#include<cstring>
#define x first
#define y second
using namespace std;
const int N=100010;
typedef pair<int,int> PII;
int a[4][N];
int g[4][4];//存储每一行中最大的三个数
PII s[4][4];//存储每一行中最大的三个数的坐标
int n;
void solve(){
	cin>>n;
	memset(g,0,sizeof g);
	for(int i=1;i<=3;i++){
		int &d1=g[i][1],&d2=g[i][2],&d3=g[i][3];//存储每一行的最大值、次大值、第三大值 
		PII &s1=s[i][1],&s2=s[i][2],&s3=s[i][3];//三个数的坐标
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
			if(a[i][j]>=d1){
				d3=d2,d2=d1,d1=a[i][j];
				s3=s2,s2=s1,s1={i,j};
			}else if(a[i][j]>=d2){
				d3=d2,d2=a[i][j];
				s3=s2,s2={i,j};
			}else if(a[i][j]>d3){
				d3=a[i][j];
				s3={i,j};
			}
		}
	}
	//每一行的最大的三个及其下标已经找出来了
	int ans=0;
	for(int i=1;i<=3;i++)//第一行 
		for(int j=1;j<=3;j++)//第二行 
			for(int k=1;k<=3;k++){//第三行 
				PII s1=s[1][i],s2=s[2][j],s3=s[3][k];
				if(s1.y!=s2.y&&s1.y!=s3.y&&s2.y!=s3.y)//它们不在同一列 
					ans=max(ans,g[1][i]+g[2][j]+g[3][k]); 
			}
	cout<<ans<<endl;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

E1 和 E2

题意分析:

如果一共有n种颜色,则操作n次一定会结束游戏,因为每次操作必定会有至少一方该颜色的弹珠数变为0

Alice想赢(分数差值尽可能大)

Bob想赢(分数差值尽可能小)

对于颜色为i的弹珠

1、Alice进行操作,则对答案的贡献为a[i]-1

2、Bob进行操作,则对答案的贡献为-(b[i]-1)

二者的差值为(a[i]-1)+(b[i]-1)

所以:

Alice选i,答案差值增加了a[i]-1+b[i]-1

Bob选i,答案差值减少了a[i]-1+b[i]-1

所以每次不管谁操作时,挑a[i]-1+b[i]-1的最大值即可

C++代码:

#include<iostream>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
const int N=200010;
int a[N],b[N];
int n;
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[i];
	priority_queue<PII> q;//大根堆
	for(int i=1;i<=n;i++)q.push({(a[i]-1)+(b[i]-1),i});
	LL ans=0;
	for(int i=1;i<=n;i++){
		PII t=q.top();
		q.pop();
		int id=t.second;
		if(i&1)ans+=a[id]-1;//Alice操作,答案加上a[id]-1
		else ans-=b[id]-1;//Bob操作,答案加上b[id]-1
	}
	cout<<ans<<endl;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

题意分析:

对于某一棵子树,只要两个点不在一根链上,就可以组队

假设当前子树的根节点为u

maxn记录最大子树中的节点数量

sum记录所有子树的节点数量和

match记录最大子树中的最大匹配数(两人一匹配)

有两种情况:

1、maxn-match*2<=sum-maxn,则最大匹配数为sum/2

2、maxn-match*2>sum-maxn,则最大匹配数为match+sum-maxn

所以直接dfs搜以每个节点为根的子树的情况即可

C++代码:

#include<iostream>
#include<vector>
using namespace std;
typedef long long LL;
const int N=200010;
int h[N],e[N],ne[N],idx;
int size1[N],dp[N];//子树大小,最大子树能组成的队伍数
int n;
void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u){
	size1[u]=1;//以u为根的子树中的节点数一开始只有自己
	dp[u]=0;//最开始能配对的数量为0
	int maxn=0,match=0,sum=0,cnt=0;
	//sum表示所有子树节点之和,maxn表示最大子树中节点数量,match表示最大子树中已经匹配的队伍数
	//cnt用于记录节点u有几个子节点 
	for(int i=h[u];i!=-1;i=ne[i]){
		int j=e[i];
		cnt++;
		dfs(j);//这里不用判断j是否被搜过是因为这是一个有向图无环树,所以不可能往回搜
		size1[u]+=size1[j];
		dp[u]=max(dp[u],dp[j]);
		if(maxn<size1[j]){
			maxn=size1[j];
			match=dp[j];
		}
		sum+=size1[j];
	}
	if(cnt==1)return;//只有一棵子树
	if(sum-maxn>=maxn-match*2)dp[u]=sum/2;
	else dp[u]=sum-maxn+match;
}
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++)h[i]=-1;//初始化h数组
    idx=0;
    for(int i=2;i<=n;i++){
    	int x;
    	cin>>x;
    	add(x,i);
	}
    dfs(1);
    cout<<dp[1]<<endl;
}
int main(){
    int T;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值