A题
原题链接:https://codeforces.com/problemset/problem/1296/A
相关tag:简单思维
我们每次操作可以把数组中的一个数变为另一个数,那么如果这两个数同为奇数或者同为偶数的话,操作后是不会对整个数组总和的奇偶性造成影响的。只有奇数变为偶数或者偶数变为奇数的时候,才会对整个数组总和的奇偶性造成影响。
那么我们可以统计出原数组中奇数的个数记为num1,偶数的个数记为num2。
整个数组总和的奇偶性是由奇数的个数决定的,当num1为奇数的时候,整个数组的总和一开始就是奇数,无需更改。
如果num1为偶数,此时整个数组的总和为偶数,就需要让某一个奇数变为偶数,或者一个偶数变为奇数,那么就要求原数组中奇数和偶数都存在。也就是num1和num2均不为0,才能通过操作变换使得整个数组的总和从偶数变为奇数。
#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e6+7;
const double eps=1e-6;
const int mod=1e9+7;
int main()
{
IOS
int t;cin>>t;
while(t--)
{
int n;cin>>n;
int num1=0,num2=0;
while(n--)
{
int x;cin>>x;
if(x&1) num1++;//判断x是奇数还是偶数
else num2++;
}
if(num1&1) cout<<"YES"<<endl;//如果num1是奇数,原数组中奇数出现了奇数次,数组总和为奇数,无需操作已经满足
else if(num1&&num2) cout<<"YES"<<endl;//num1是偶数,原数组总和为偶数,需要奇数和偶数均存在才能改变总和的奇偶性
else cout<<"NO"<<endl;
}
}
B题
原题链接:https://codeforces.com/problemset/problem/1296/B
相关tag:暴力,模拟,贪心
当然你也可以选择推公式,都可以,这里给出暴力做法。
我们贪心策略,当前手上个位数上的钱都先放着不用,其余的钱都拿去买,拿了返利后,重复之前的操作,直到钱的数量小于10无法得到返利为止。
容易证得,最多两次上述操作,就可以使得我们手上剩余钱的位数-1,因此暴力计算时间完全足够。
#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e6+7;
const double eps=1e-6;
const int mod=1e9+7;
int main()
{
IOS
int t;cin>>t;
while(t--)
{
int n;cin>>n;
int ans=0;
while(n>9)//当还可以得到返利时就不断重复把除了个位的钱都花掉,贪心去拿返利
{
ans+=n/10*10;
n=n%10+n/10;
}
cout<<ans+n<<endl;
}
}
C题
原题链接:https://codeforces.com/problemset/problem/670/C
相关tag:离散化,《算法竞赛进阶指南》第33页例题原题。
当然也可以用map来做,这里还是更希望能去学习一下离散化。
注意到这道题数据虽然只有2e5个,但是数值非常大,最大有1e9,我们是开不了1e9大小的数组来直接记录每个数出现了几次的。
但是数组也就2e5个,加上下面安排中的数字,总共也就最多6e5种不同的数字。可以通过离散化把这些数字映射到1-6e5这6e5个整数里。
离散化具体操作去看书或者博客去学吧。这里给出stl的实现和数组实现两种方案。
stl实现的离散化代码:
#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=2e5+7;
const double eps=1e-6;
const int mod=1e9+7;
vector<int>origin;//用于离散化
int find(int x)//在离散化数组中查找当前x所在的位置,这里也可以自己手写个二分
{
return (int)(lower_bound(origin.begin(),origin.end(),x)-origin.begin());
}
int num[maxn];//记录m个数字
int cas[maxn*3];//cas[i]记录origin[i]代表的数字出现了几次,注意这里要开三倍大小,因为后面m种安排的节目数字不一定在num中出现过
int x[maxn],y[maxn];//x[i]代表第i种安排的第一个节目,y[i]是第i种安排的第二个节目
int feichang[maxn],yiban[maxn];//feichang[i]代表第i种安排非常开心的有几个人,yiban[i]代表第i种安排一般开心的有多少个人
int n;
int main()
{
IOS
cin>>n;
for(int i=1;i<=n;i++){cin>>num[i];origin.push_back(num[i]);}
int m;cin>>m;
for(int i=1;i<=m;i++) {cin>>x[i];origin.push_back(x[i]);}
for(int i=1;i<=m;i++) {cin>>y[i];origin.push_back(y[i]);}//注意要把安排里的数字也压入origin,因为这些数字可能在前面的num数组中未出现
sort(origin.begin(),origin.end());
origin.erase(unique(origin.begin(),origin.end()),origin.end());//离散化过程
for(int i=1;i<=n;i++) cas[find(num[i])]++;//统计每个数出现了几次
int ans=1;
for(int i=1;i<=m;i++)
{
feichang[i]=cas[find(x[i])];
yiban[i]=cas[find(y[i])];
if(feichang[i]>feichang[ans]||feichang[i]==feichang[ans]&&yiban[i]>yiban[ans]) ans=i;
}
cout<<ans<<endl;
}
数组实现的离散化代码:
#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=2e5+7;
const double eps=1e-6;
const int mod=1e9+7;
int origin[maxn*3];//用于离散化
int tot=0,now=1;//tot代表离散化前origin数组中保存了几个数,now代表离散化后origin数组中保存了几个数
int find(int x)//在离散化数组中查找当前x所在的位置,这里也可以自己手写个二分
{
return (int)(lower_bound(origin,origin+now,x)-origin);
}
int num[maxn];//记录m个数字
int cas[maxn*3];//cas[i]记录origin[i]代表的数字出现了几次,注意这里要开三倍大小,因为后面m种安排的节目数字不一定在num中出现过
int x[maxn],y[maxn];//x[i]代表第i种安排的第一个节目,y[i]是第i种安排的第二个节目
int feichang[maxn],yiban[maxn];//feichang[i]代表第i种安排非常开心的有几个人,yiban[i]代表第i种安排一般开心的有多少个人
int n;
int main()
{
IOS
cin>>n;
for(int i=1;i<=n;i++){cin>>num[i];origin[tot++]=num[i];}
int m;cin>>m;
for(int i=1;i<=m;i++) {cin>>x[i];origin[tot++]=x[i];}
for(int i=1;i<=m;i++) {cin>>y[i];origin[tot++]=y[i];}//注意要把安排里的数字也压入origin,因为这些数字可能在前面的num数组中未出现
sort(origin,origin+tot);
for(int i=1;i<tot;i++)
if(origin[i]!=origin[now-1]) origin[now++]=origin[i];//离散化过程
for(int i=1;i<=n;i++) cas[find(num[i])]++;//统计每个数出现了几次
int ans=1;
for(int i=1;i<=m;i++)
{
feichang[i]=cas[find(x[i])];
yiban[i]=cas[find(y[i])];
if(feichang[i]>feichang[ans]||feichang[i]==feichang[ans]&&yiban[i]>yiban[ans]) ans=i;
}
cout<<ans<<endl;
}
D题
原题链接:https://codeforces.com/problemset/problem/892/B
相关tag:双指针,或者也可以叫尺取法,滑动窗口,名字并不重要,掌握思想是关键。
这里我们可以从后往前看,用now来记录当前仍然为黑色的数字中,最右侧的是哪一个,num[i]记录第i个数字会把自己前面几个数染成白色。
随着循环借助当前的位置i减去num[i]-1,得到i位置对前面的进行染色后,仍然为黑色的最右侧下标是多少,和now比较并更新now。
循环内的操作都是O(1)的,总复杂度O(n)
#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e6+7;
const double eps=1e-6;
const int mod=1e9+7;
const int xx=1;
int n;
int num[maxn];
int main()
{
IOS
cin>>n;
for(int i=1;i<=n;i++) cin>>num[i];
int ans=0;
int now=n;//now记录当前仍然为黑色的数字中,最右侧的是哪一个
for(int i=n;i>0;i--)//从后往前看
{
if(i<=now) ans++;//如果当前下标在i前,代表当前位置仍然为黑色
if(num[i]) now=min(now,i-num[i]-1);//更新now
}
cout<<ans<<endl;
}
E题
原题链接:https://codeforces.com/problemset/problem/520/B
相关tag:数学,dfs/bfs跑最短路径
这道题结论做法当然好,但是我还是更希望大家能看一下,这种问题是怎么样转换成一个图论可以解决的模型的,并且学习一下dfs/bfs算法。
这道题我们可以把每个值看成一个点,那么点之间的边是什么呢,那就是x到x-1,x到2x,有这两类边,对应题目给我们的两种操作。
我们建图之后直接找x到y的最短路径就行了,可以借助bfs或者dfs剪枝来是实现。
dfs实现:
#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e6+7;
const double eps=1e-6;
const int mod=1e9+7;
int x,y;
int dis[maxn];//dis[i]记录从x到i最少要几次操作
void dfs(int now,int d)//now代表当前数字是多少,d代表从x到now经过了多少次操作
{
if(dis[now]<=d) return;//如果now数字已经有了更优的到达方式,不再进行下面的操作
dis[now]=d;//更新x到now需要的最少次数
if(now>=y) {dis[y]=min(dis[y],d+now-y);return;}//如果当前数值已经大于等于目标y了,就没必要再乘2了,直接-1到值y为止
dfs(now*2,d+1);//两种操作,一种是当前数值乘以2
if(now>1) dfs(now-1,d+1);//另一种操作,当前数值-1
}
int main()
{
IOS
cin>>x>>y;
for(int i=0;i<maxn;i++) dis[i]=INF;
dfs(x,0);
cout<<dis[y]<<endl;
}
bfs实现:
#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e6+7;
const double eps=1e-6;
const int mod=1e9+7;
int x,y;
int dis[maxn];//dis[i]记录从x到i最少要几次操作
bool flag[maxn];//flag[i]记录数字i是否已经进过bfs过程的队列
void bfs()
{
queue<int>Q;
Q.push(x);
dis[x]=0;
while(Q.size())
{
int now=Q.front();
Q.pop();
if(now==y) break;//如果已经走到数字y了,结束bfs过程
if(!flag[now*2])
{
if(now*2>=y) dis[y]=min(dis[y],dis[now]+1+now*2-y);
else
{
Q.push(now*2);
dis[now*2]=dis[now]+1;
flag[now*2]=1;
}
}
if(now>1&&!flag[now-1])
{
Q.push(now-1);
dis[now-1]=dis[now]+1;
flag[now-1]=1;
}
}
}
int main()
{
IOS
cin>>x>>y;
dis[y]=INF;
if(x>=y) dis[y]=x-y;
else bfs();
cout<<dis[y]<<endl;
}
F题
原题链接:
相关tag:离线,并查集
离线之后反向操作,就把删点变成加点了,然后就可以用并查集来解决问题了。
懂得自然懂,直接丢代码。
#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+7;
const double eps=1e-6;
const int mod=1e9+7;
bool flag[maxn];
ll num[maxn];
ll ans[maxn];
int ope[maxn];
int n;
int fa[maxn];
ll sum[maxn];
int ask(int x) {return fa[x]==x?x:fa[x]=ask(fa[x]);}
void merge(int x,int y)
{
fa[x]=y;
sum[y]+=sum[x];
}
int main()
{
IOS
cin>>n;
for(int i=1;i<=n;i++) cin>>num[i];
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=n;i++) cin>>ope[i];
ans[n]=0;
for(int i=n;i>1;i--)
{
ans[i-1]=ans[i];
flag[ope[i]]=1;
sum[ope[i]]+=num[ope[i]];
ans[i-1]=max(ans[i-1],sum[ope[i]]);
if(ope[i]>1&&flag[ope[i]-1])
{
merge(ask(ope[i]),ask(ope[i]-1));
ans[i-1]=max(ans[i-1],sum[ask(ope[i])]);
}
if(ope[i]<n&&flag[ope[i]+1])
{
merge(ask(ope[i]),ask(ope[i]+1));
ans[i-1]=max(ans[i-1],sum[ask(ope[i])]);
}
}
for(int i=1;i<=n;i++) cout<<ans[i]<<endl;
}
G题
原题链接:https://codeforces.com/problemset/problem/1339/D
相关tag:树上构造
#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+7;
const double eps=1e-6;
const int mod=1e9+7;
bool jishu=0,oushu=0;
int dp[maxn],n;
vector<int>dir[maxn];
void dfs(int pre,int now,int deep)
{
bool leaf=0;
if(dir[now].size()==1)
{
if(deep&1) jishu=1;
else oushu=1;
}
for(int i=0;i<dir[now].size();i++)
{
if(dir[now][i]==pre) continue;
dfs(now,dir[now][i],deep+1);
if(dir[dir[now][i]].size()==1) leaf=1;
else dp[now]+=dp[dir[now][i]]+1;
}
dp[now]+=leaf;
}
int main()
{
IOS
cin>>n;
for(int i=1;i<n;i++)
{
int a,b;cin>>a>>b;
dir[a].push_back(b);
dir[b].push_back(a);
}
int tar=-1;
for(int i=1;i<=n;i++)
if(dir[i].size()>1) {tar=i;break;}
dfs(-1,tar,0);
if(jishu&&oushu) cout<<3<<' ';
else cout<<1<<' ';
cout<<dp[tar]<<endl;
}