A
题意分析:
看思考每个题的时间是否大于等于解决该题需要的时间,如果是,则能解决该题,否则不能,直接枚举计数即可
C++代码:
cpp
#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++代码:
cpp
#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++代码:
cpp
#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;
}
D
题意分析:
先找出每种活动人数最多的三天,即找出每一行中最大的三个数
显然,最优解一定是从这些数中每一行取一个,前提是这三个数不能在同一列,所以在存储最大的三个数时还要存一下坐标
C++代码:
cpp
#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++代码:
cpp
#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;
}
F
题意分析:
对于某一棵子树,只要两个点不在一根链上,就可以组队
假设当前子树的根节点为u
maxn记录最大子树中的节点数量
sum记录所有子树的节点数量和
match记录最大子树中的最大匹配数(两人一匹配)
有两种情况:
1、maxn-match*2<=sum-maxn,则最大匹配数为sum/2
2、maxn-match*2>sum-maxn,则最大匹配数为match+sum-maxn
所以直接dfs搜以每个节点为根的子树的情况即可
C++代码:
cpp
#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;
}