目录
Codeforces Round #617 (Div. 3) 题解
前言
这次比赛的题目都好有意思啊。STL真好用
包含题目:
CF 1296A,CF 1296B,CF 1296C,CF 1296D,CF 1296E1,CF 1296E2,CF 1296F
A. Array with Odd Sum
题意
给你一个序列,其中可以把任意一个的元素的值赋给任意一个另外的元素,这个操作可以进行任意次,也可不进行。问你能否把它们的和变成一个奇数。
做法
我们先计算奇数和偶数的个数,之后分类讨论:
- 全是单数
- 如果\(n\)是偶数,就输出
NO
- 如果\(n\)是奇数,就输出
YES
- 如果\(n\)是偶数,就输出
- 在有至少一个偶数的情况下
- 如果没有奇数,输出
NO
- 如果有至少一个奇数,输出
YES
- 如果没有奇数,输出
程序
其中n&1
可以简单的认为等价于n%2
,但是运算优先级不一样。
#include<bits/stdc++.h>
using namespace std;
int t,n,a[2005];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
int odd=0,even=0;
for(int i=0;i<n;i++){
cin>>a[i];
if(a[i]&1){
odd++;
}else{
even++;
}
}
if(even==0){
if(n&1){
cout<<"YES"<<endl;
}else{
cout<<"NO"<<endl;
}
}else{
if(odd==0){
cout<<"NO"<<endl;
}else{
cout<<"YES"<<endl;
}
}
}
return 0;
}
B. Food Buying
题意
给你一个数\(s\)为初始钱数,你每次花\(x\)元,花完后可以返还\(\lfloor \frac x {10} \rfloor\)元(\(x\)除以\(10\)下取整)。问你最多能花出去多少钱。
做法
每次把个位数上的零头留着,花掉十位数往上的整钱,把返还的钱数和当前的钱数合并,再继续花下去。直到你只有零钱了,就全部花掉。
程序
#include<bits/stdc++.h>
using namespace std;
int t,n,ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
ans=0;
while(n>=10){
ans+=n-n%10;
n=n/10+n%10;
}
cout<<ans+n<<endl;
}
return 0;
}
C. Yet Another Walking Robot
题意
给你一个机器人,每一步可以上下左右走,再给一个机器人的移动步骤的字符串,让你删除一个非空子串(连续的),使得机器人停止的位置和没有删除之前停止的位置相同。
做法
前缀和,得到执行了每一步所停在的位置,如果有两个不同的字符串中的位置使得机器人停在的位置相同,那么可以删去第一个字符串中的位置往后一个到第二个字符串位置的子串。特别的,机器人未开始时在的位置也要计算进去。
程序
#include<bits/stdc++.h>
using namespace std;
int t,n;
string s;
int px;
int py;
map<pair<int,int>,set<int> > mp;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
cin>>s;
px=py=0;
mp.clear();
mp[make_pair(0,0)].insert(-1);
for(int i=0;i<n;i++){
px=px+(s[i]=='R')-(s[i]=='L');
py=py+(s[i]=='U')-(s[i]=='D');
mp[make_pair(px,py)].insert(i);
}
int ansl,ansr,ans=1e9;
for(map<pair<int,int>,set<int> >::iterator it=mp.begin();it!=mp.end();it++){
if(it->second.size()>1){
vector<int> v;
for(set<int>::iterator i=it->second.begin();i!=it->second.end();i++){
v.push_back(*i);
}
for(int l=0,r=0;l<v.size();l++){
while(r<v.size()&&v[r]-v[l]<=0)r++;
if(r==v.size())break;
if(v[r]-v[l]<ans){
ans=v[r]-v[l];
ansl=v[l]+2;
ansr=v[r]+1;
}
}
}
}
if(ans==1e9){
cout<<-1<<endl;
}else{
cout<<ansl<<' '<<ansr<<endl;
}
}
return 0;
}
D. Fight with Monsters
题意
\(n\)个怪物,每个血量\(h_i\),你攻击力\(a\),你对手攻击力\(b\),怪物血量小于等于\(0\)时死亡。遇到怪物时你先手,你对手后手,轮流攻击,造成最后一击的人拿到分数。你可以跳过你对手的回合\(k\)次,使他不能攻击。你们从第一个怪物打到第\(n\)个,问你能拿到的最大的分数。
做法
对于每个怪物,我们计算你造成最后一击所需要的跳过对手回合的次数:
- 首先把怪物的血量对\(a+b\)取模,之后如果是\(0\),那么再加上\(a+b\),保证不跳过的话,你和对手攻击过后怪物必定死亡。
- 然后计算你要打\(c_i\)次杀死怪物,那么显然需要跳过你的对手\(c_i-1\)次。
把\(c_i-1\)的数组排个序从小到大贪心的使用\(k\)次机会即可。
程序
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,a,b,k;
ll h[200005];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>a>>b>>k;
for(ll i=0;i<n;i++){
cin>>h[i];
h[i]%=a+b;
if(h[i]==0)h[i]+=a+b;
h[i]=(h[i]-1)/a;
}
sort(h,h+n);
ll ans=0;
for(int i=0;i<n;i++){
k-=h[i];
if(k>=0)ans++;
else break;
}
cout<<ans<<endl;
return 0;
}
E1. String Coloring (easy version)
题意
给你一个字符串,让你把它涂成两种颜色,可以交换相邻的不同颜色的元素,问你能不能把它搞成有序的。
做法
显然交换相邻是冒泡排序的思想。可以看出,消除逆序对,需要它们的颜色不同,于是,我们可以分别维护颜色为\(0\)和\(1\)的目前元素的最大值,从前到后的处理字符串,贪心的先涂成\(0\),假如\(0\)中元素的最大值比它大,那么就会造成不能排序,此时就涂成\(1\),假如还是有最大值比它大,那么就输出-1
,否则就更新当前颜色的最大值,就可以了。
程序
#include<bits/stdc++.h>
using namespace std;
int n;
string s;
int col[205];
int cnt,mx[2];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
cin>>s;
for(int i=0;i<n;i++){
if(mx[0]>s[i]){
if(mx[1]>s[i]){
cout<<"NO"<<endl;
return 0;
}else{
mx[1]=s[i];
col[i]=1;
}
}else{
mx[0]=s[i];
col[i]=0;
}
}
cout<<"YES"<<endl;
for(int i=0;i<n;i++)cout<<col[i];
cout<<endl;
return 0;
}
E2. String Coloring (hard version)
题意
请照着E1理解。现在你可以涂无限多种颜色,求搞成有序序列最少要涂多少种不同的颜色。
做法
基本想法还是相同的。消除逆序对需要两个端点是不同的颜色,于是我们就记录每一种颜色对应的最大值,保存为数组。我们把保存颜色\(i\)中最大的元素的数组叫作\(mx_i\),对于每一位\(s_i\)的更新,我们需要快速的查询这个数组中第一个比\(s_i\)小或者相同的元素位置。由于每一次更新第\(i\)位时,保证\(mx\)前面的元素都比\(i\)大,所以这个数组单调递减,可以二分地找到这个位置。
程序
#include<bits/stdc++.h>
using namespace std;
int n,sz,ans;
char s[200005];
int col[200005];
int mx[200005];
int qry(int val){
int l=1,r=n,m,res;
while(l<=r){
m=(l+r)>>1;
if(mx[m]<=val){
res=m;
r=m-1;
}else{
l=m+1;
}
}
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
sz=1;while(sz<n)sz<<=1;
cin>>s+1;
for(int i=1;i<=n;i++){
int pos=qry(s[i]);
mx[pos]=s[i];
col[i]=pos;
ans=max(ans,pos);
}
cout<<ans<<endl;
for(int i=1;i<=n;i++)cout<<col[i]<<' ';
cout<<endl;
return 0;
}
F. Berland Beauty
题意
给你一棵树,每条边有一个权值,告诉你数个条件,每个条件告诉你某两点之间的简单路径上的所有的边中权值最小的一条边的权值,问你能否构建出这样的满足所有条件的树,并输出可行方案。
做法
由于数据范围十分的小,所以暴力就好了。
我们首先规定\(1\)是这棵树的根,然后预处理每个节点的深度。然后,两个节点\(u,v\)间的简单路径,必定是\(u\)到\(lca(u,v)\)和\(v\)到\(lca(u,v)\)的这段路程的和,此处\(lca(u,v)\)是\(u,v\)的最近公共祖先(有根树意义下)。于是简单路径便可以这么求得:把较深的节点通过不断的变换为它的父亲的方式提上来,之后同时提两个节点,直到变成同一个。经过的边就是它们之间的简单路径。
对于每一条边,为了满足条件,它的权值必定是所有影响它的条件中的权值的最大值,我们直接把它的权值就固定了,假如没有影响它的边,我们就赋值为任意的值。比如114514
最后我们在查看一遍每个条件是否满足了就好了。
程序
#include<bits/stdc++.h>
using namespace std;
int n,m;
vector<pair<int,int> > g[5005];
pair<pair<int,int>,int> q[5005];
int d[5005];
pair<int,int> par[5005];
int f[5005];
void dfs(int x,int p,int cd){
d[x]=cd;
for(int i=0;i<g[x].size();i++){
int &y=g[x][i].first,&z=g[x][i].second;
if(y!=p){
dfs(y,x,cd+1);
par[y]=make_pair(x,z);
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
g[a].push_back(make_pair(b,i));
g[b].push_back(make_pair(a,i));
f[i]=1;
}
dfs(1,-1,1);
cin>>m;
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
q[i]=make_pair(make_pair(a,b),c);
if(d[a]>d[b])swap(a,b);
while(d[b]>d[a]){
f[par[b].second]=max(f[par[b].second],c);
b=par[b].first;
}
while(b!=a){
f[par[a].second]=max(f[par[a].second],c);
a=par[a].first;
f[par[b].second]=max(f[par[b].second],c);
b=par[b].first;
}
}
for(int i=1;i<=m;i++){
bool fl=false;
int a,b,c;
a=q[i].first.first;
b=q[i].first.second;
c=q[i].second;
if(d[a]>d[b])swap(a,b);
while(d[b]>d[a]){
if(f[par[b].second]==c)fl=true;
b=par[b].first;
}
while(b!=a){
if(f[par[a].second]==c)fl=true;
a=par[a].first;
if(f[par[b].second]==c)fl=true;
b=par[b].first;
}
if(!fl){
cout<<-1<<endl;
return 0;
}
}
for(int i=1;i<n;i++){
cout<<f[i]<<' ';
}
cout<<endl;
return 0;
}
谢谢观看
emmm,觉得E题的贪心写得很烂,主要赛时突然就想到了,感性理解一波就直接交了,有什么不懂的可以问啦。
会有人看&问吗