left 2 Codeforces Round 916 (Div. 3)
A.
遍历字符串,用map记录下每个字符出现的次数
最后遍历26个字母,若出现了相应次数答案+1
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
void solve() {
int n;cin>>n;
string s;cin>>s;
map<char,int>mp;
for(int i=0;i<s.size();i++){
mp[s[i]]++;
}
// for(auto t:mp){
// cout<<t.first<<' '<<t.second<<'\n';
// }
int ans=0;
for(int i=0;i<26;i++){
if(mp[(char)('A'+i)]>=i+1)ans++;
}
cout<<ans<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int left=1;
cin>>left;
while(left--){
solve();
}
}
B.
升序能让他兴奋,倒序不会
让k+1是升序的,剩下的倒序
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
void solve() {
int n,k;cin>>n>>k;
int tmp=n-k;
for(int i=tmp;i<=n;i++)cout<<i<<' ';
for(int i=tmp-1;i>=1;i--)cout<<i<<' ';
cout<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int left=1;
cin>>left;
while(left--){
solve();
}
}
C.
枚举+贪心
初步思路一直想着一次贪完,发现无法做
然后考虑枚举+贪心
枚举的是要顺序完成几个任务,贪心的是维护b数组的最大值
去掉顺序完成任务的必须天数,剩下的全部去完成最大值
debug:
注意k和n的大小关系,可能枚举不完
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
void solve() {
int n,k;cin>>n>>k;
vector<int>a(n+1),b(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
int ans=0,ma=0,pre=0,tmp;
for(int i=1;i<=min(n,k);i++){
ma=max(ma,b[i]);
tmp=pre+a[i]+max(0ll,(k-i)*ma);
ans=max(ans,tmp);
pre+=a[i];
}
cout<<ans<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int left=1;
cin>>left;
while(left--){
solve();
}
}
D.
枚举+贪心
贪心:若我们选择了一天,那么这一天一定是可选的天数中a或b或c最大的那一天
枚举:a优先再b优先再c优先
a优先再c优先再b优先
......
3的全排列
也就是按a优先的前3个与b的前3个,c的前3个的全排列
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
struct node{
int a,b,c;
}g[N];
bool cmp1(node x,node y){
return x.a>y.a;
}
bool cmp2(node x,node y){
return x.b>y.b;
}
bool cmp3(node x,node y){
return x.c>y.c;
}
void solve() {
int n;cin>>n;
for(int i=1;i<=n;i++)cin>>g[i].a;
for(int i=1;i<=n;i++)cin>>g[i].b;
for(int i=1;i<=n;i++)cin>>g[i].c;
int ma=0,tmp=0;
sort(g+1,g+1+n,cmp1);
tmp+=g[1].a;
sort(g+2,g+1+n,cmp2);
tmp+=g[2].b;
sort(g+3,g+1+n,cmp3);
tmp+=g[3].c;
ma=max(ma,tmp);
tmp=0;
sort(g+1,g+1+n,cmp1);
tmp+=g[1].a;
sort(g+2,g+1+n,cmp3);
tmp+=g[2].c;
sort(g+3,g+1+n,cmp2);
tmp+=g[3].b;
ma=max(ma,tmp);
tmp=0;
sort(g+1,g+1+n,cmp2);
tmp+=g[1].b;
sort(g+2,g+1+n,cmp1);
tmp+=g[2].a;
sort(g+3,g+1+n,cmp3);
tmp+=g[3].c;
ma=max(ma,tmp);
tmp=0;
sort(g+1,g+1+n,cmp2);
tmp+=g[1].b;
sort(g+2,g+1+n,cmp3);
tmp+=g[2].c;
sort(g+3,g+1+n,cmp1);
tmp+=g[3].a;
ma=max(ma,tmp);
tmp=0;
sort(g+1,g+1+n,cmp3);
tmp+=g[1].c;
sort(g+2,g+1+n,cmp1);
tmp+=g[2].a;
sort(g+3,g+1+n,cmp2);
tmp+=g[3].b;
ma=max(ma,tmp);
tmp=0;
sort(g+1,g+1+n,cmp3);
tmp+=g[1].c;
sort(g+2,g+1+n,cmp2);
tmp+=g[2].b;
sort(g+3,g+1+n,cmp1);
tmp+=g[3].a;
ma=max(ma,tmp);
tmp=0;
cout<<ma<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int left=1;
cin>>left;
while(left--){
solve();
}
}
E.
贪心地想,有两种情况:
自己得的多但对方丢的少,自己得的少但对方丢的多
如何合并?
把自己得的和对方丢的都算自己得的即可
由题意可知,一种颜色只会由一个人操作,而不管由谁操作得到的总贡献是一样的
那么我们直接按贡献排个序,再按轮次记录下两人实际得到的弹珠数,最终结果相减即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
struct node{
int sum,a,b;
};
bool cmp(node x,node y){
return x.sum>y.sum;
}
void solve() {
int n;cin>>n;
vector<int>a(n+1),b(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
vector<node>g(n+1);
for(int i=1;i<=n;i++){
g[i].sum=a[i]+b[i]-1;
g[i].a=a[i];g[i].b=b[i];
}
sort(g.begin()+1,g.end(),cmp);
int alice=0,bob=0;
for(int i=1;i<=n;i++){
if(i&1){
alice+=g[i].a-1;
}else{
bob+=g[i].b-1;
}
}
cout<<alice-bob<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int left=1;
cin>>left;
while(left--){
solve();
}
}
F.
把题目所给的关系看成一棵树
也就是从根节点到叶子结点的简单路径上的点不能组队
其特点是只有叶子节点的出度为0
那么我们每次找两个叶子结点组队即可
可以先预处理出每个节点的深度,深度大的优先组队
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
int n;
vector<int>g[N];
int f[N],out[N],dep[N];
void dfs(int u,int step){
dep[u]=step;
for(int t:g[u])dfs(t,step+1);
}
void solve() {
cin>>n;
vector<int>p(n+2);
for(int i=2;i<=n;i++){
cin>>f[i];
g[f[i]].push_back(i);
out[f[i]]++;
}
dfs(1,1);
priority_queue<pair<int,int>>q;
for(int i=1;i<=n;i++){
if(out[i]==0)q.push({dep[i],i});
}
int ans=0;
while(q.size()>=2){
auto x=q.top();q.pop();
auto y=q.top();q.pop();
ans++;
if(--out[f[x.second]]==0&&f[x.second]!=1){
q.push({dep[f[x.second]],f[x.second]});
}
if(--out[f[y.second]]==0&&f[y.second]!=1){
q.push({dep[f[y.second]],f[y.second]});
}
}
cout<<ans<<'\n';
for(int i=1;i<=n;i++){
g[i].clear();
f[i]=0;out[i]=0;dep[i]=0;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int left=1;
cin>>left;
while(left--){
solve();
}
}
G1/G2.
先考虑S集合的最小大小
自己思路是遇到不同颜色的灯泡tmp值+1,遇到相同颜色灯泡tmp值-1,党tmp为0时说明不能再拓展了
题解提供了异或的方法,两个相同的数异或为0
所以这个过程可以用异或来代替
异或还有个好处是两个相同的数异或才为0
而加加减减没有辨识度
再考虑连通块内部的小连通块
这其实是局部区间消除,那么我们可以记录异或前缀
若有相同的出现,把这段标记
最后统计答案时不再统计这段即可
这里吧1-n用随机数去映射,可以降低哈希冲突
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
const int mod=998244353;
mt19937_64 rnd(98275314);
int get(){
int x=0;
while(x==0)x=rnd();
return x;
}
vector<int>c,g;
int block(int l,int r){
int ans=0;
while(l<r){
if(g[l]!=-1&&g[l]<r){
l=g[l];
}else {
ans++;
l++;
}
}
return ans;
}
void solve() {
int n;cin>>n;
int size=0,cnt=1;
c.resize(n*2);
g.resize(n*2,-1);
for(int i=0;i<2*n;i++){
cin>>c[i];c[i]--;
}
vector<int>val(n);
for(int i=0;i<n;i++)val[i]=get();
map<int,int>last;
int cur=0;
last[0]=0;
for(int i=0;i<n*2;i++){
cur^=val[c[i]];
if(cur==0){
size++;
cnt=(cnt* block(last[0],i+1))%mod;
last.clear();
}else if(last.count(cur))g[last[cur]]=i+1;
last[cur]=i+1;
}
cout<<size<<' '<<cnt<<'\n';
c.clear();g.clear();
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int left=1;
cin>>left;
while(left--){
solve();
}
}