cf #796 Div.2
文章目录
- cf #796 Div.2
- [A. Cirno's Perfect Bitmasks Classroom](https://codeforces.com/contest/1688/problem/A)
- [B. Patchouli's Magical Talisman](https://codeforces.com/contest/1688/problem/B)
- [C. Manipulating History](https://codeforces.com/contest/1688/problem/C)
- [D. The Enchanted Forest](https://codeforces.com/contest/1688/problem/D)
A. Cirno’s Perfect Bitmasks Classroom
- 题意
找到一个最小的y使得x&y>0 && x^y>0
-
题解
- 要使x&y>0中y的最小值,只要让x的最低位1在y的对应位上也为1即可。所以满足此式的最小y为lowbit(x)。
- 要使x^y>0,只要x,y中存在某对应位是相异的即可。如果上述所求的y已经满足要求了,则不用计算,因为这个条件的运算还可能使y增大。如果上述求的y不满足此式,要么让x的最低位1在y的对应位上为0(不可能,与上一条矛盾),要么让x的最低位0在y的对应位置上为1。
- 同时满足上述两个条件的y就是y1+y2
-
代码
#include <iostream>
#include <cmath>
using namespace std;
typedef long long LL;
int lowbit(int x){
return x&-x;
}
void solve()
{
int x;
cin>>x;
int a=lowbit(x);//求y1
if(x^a){//y1已经符合2直接输出
cout<<a<<'\n';
return ;
}
//y1不符合2
int i,b;
for(i=0;i<=30;i++){//找最低位0
if(x&1)x=x>>1;
else break;
}
b=pow(2,i);//求得y2
cout<<a+b<<'\n';//y=y1+y2
}
int main()
{
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
B. Patchouli’s Magical Talisman
-
题意
给定一个整数数组,可以进行两种操作:1.删除任意两个数a,b,同时增加一个数a+b;2.选定某数除以2。输出让所有数都变奇数的最小操作步数。
-
题解
-
如果数组中既有奇数又有偶数,那么把一个偶数变奇数只需要选一个奇数与这个偶数相加,最小步数为偶数的个数。因为把一个偶数变成奇数的最小步数为1,所以这样奇偶相加变换已经是最优方案。
-
如果数组中全是偶数,只需选一个偶数先变成奇数就划归成上一情况,那么这个偶数需选lowbit最小的(lowbit的1的位置代表这个数变成奇数的次数)即变成奇数所需次数最小。
-
-
代码
#include <iostream>
using namespace std;
int lowbit(int x){
return x&-x;
}
int main()
{
int T;
cin>>T;
while(T--){
int n,x,odds=0,evens=0,min_lowbit_even=0x3f3f3f3f;
cin>>n;
for(int i=1;i<=n;i++){
cin>>x;
if(x&1)odds++;
else {
evens++;
min_lowbit_even=min(min_lowbit_even,lowbit(x));
}
}
if(odds)cout<<evens<<'\n';
else {
int a=-1;//最小lowbit变成奇数所需的次数
while(min_lowbit_even){
a++;
min_lowbit_even>>=1;
}
cout<<n-1+a<<'\n';
}
}
return 0;
}
C. Manipulating History
-
题意
初始串只有1个字母,每次选择任意子串将其替换成其他串,给出打乱顺序的操作序列和最终结果,问字符串中最初的字母是什么
-
题解
统计每个字母出现的次数,出现奇数次的字母为初始字母。
因为每个字母在被添加进来时会出现一次,当被替换删除的的时候会再出现一次或者一直保留到最终字符串中也会出现一次,故一定出现偶数次。而最初的字母不需被添加,所以只会在被替换删除或者保留到最后字符串中出现一次,即最初字母只出现奇数次。
-
代码
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
int T;
cin>>T;
while(T--){
int n;
int a[26]={0};
string str;
cin>>n;
n=2*n+1;
while(n--){
cin>>str;
int len=str.length();
for(int i=0;i<len;i++)a[str[i]-'a']++;
}
for(int i=0;i<26;i++)
if(a[i]&1)
cout<<(char)(i+'a')<<'\n';
}
return 0;
}
D. The Enchanted Forest
-
题意
给出一个分数序列,可以任选一个初始位置,每次可以向左/向右/不动,然后取走这个位置上的全部分数,同时所有位置分数+1,问走k步的分数最大值为多少
-
题解
对于位置i,其对分数的贡献为a[i]+(t[i]-1),t[i]表示最后一次到达i位置的时间(因为可能会多次经过i,所以在i位置的收获的多长出来的分数为t[i])。所以若选择的位置为pos1,pos2,pos3…,那么答案就是
∑ a [ i ] + ∑ ( t [ i ] − 1 ) , 上下限取决于经过的点个数 \sum{a[i]}+\sum{(t[i]-1)},上下限取决于经过的点个数 ∑a[i]+∑(t[i]−1),上下限取决于经过的点个数- k<=n;即能选的位置最多为k个,t[i]-1的和是固定的一定是s[0,k-1],而要让答案尽量大只能让a[i]之和大,即求长度为k的连续区间和的最大值。
- k>n;即最多选的位置为n个,a[i]的和固定为前n个数的和,要让答案大只能让最后到达点的位置的时间最大,故t[i]-1取k-1,k-2,k-3…这n个到达的时间点
-
代码
#include <iostream>
using namespace std;
const int N=2e5+5;
int a[N],pre[N];
int main()
{
int T;
cin>>T;
while(T--){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
pre[i]=pre[i-1]+a[i];//前缀和
}
if(k>n){
int res=pre[n];
for(int i=1;i<=n;i++)res+=k-i;
cout<<res<<'\n';
}
else {
int res=0;
for(int i=k;i<=n;i++)res=max(res,pre[i]-pre[i-k]);
for(int i=0;i<k;i++)res+=i;
cout<<res<<'\n';
}
}
return 0;
}