Codeforces Round #769(div2)
ABCD的题解
大家好,因为小编水平限制,只会写出div2前四题的题解
A.ABC
A题链接
题意:给定长度为n的01串,可以对这个串重新任意排列,问是否存在一种排列,使得该串的任意一个长度大于1的子串都不为回文串。
思路:根据规律我们可以得到1、0、10、01均是NO,其他情况均是YES。
代码:
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n;
cin>>n;
string s;
cin>>s;
if(n==1)
{
cout<<"YES"<<endl;
return;
}
if(n==2)
{
if(s[0]!=s[1])
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
return ;
}
cout<<"NO"<<endl;
}
int main()
{
int t;
cin>>t;
while(t--)
{
solve();
}
}
B.Roof Construction
B题链接
题意:题意:给定一个n,表示有一个长度为n的数组p,元素是 0,1,2,…,n−10,1,2,…,n−1,要求对这个数组重新排列,使得pi xor pi+1的最大值最小,输出排列后的数组。
思路:我们设答案为k,则我们先让[0 k]先配对,然后把剩下的的数分两组,[1 k-1]和[k+1,n-1]
第一组的任意排列,其相邻的xor值都不会大于k,第二组也是,因为最高位1被消掉了。
那么就是边界问题了,主要是第二组,一定会有一个数字是和别的数字接触的,那么我们用k去接触,把它的最高位消掉即可。
那么就可以排列出 n−1,n−2,…,k,0,1,2,…,k−1
代码:
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n;
cin>>n;
int k=1;
while(k<n)
{
k<<=1;
}
k>>=1;
for(int i=n-1;i>=k;i--)
{
cout<<i<<" ";
}
cout<<"0 ";
for(int i=1;i<k;i++)
{
cout<<i<<" ";
}
cout<<endl;
}
int main()
{
int t;
cin>>t;
while(t--)
{
solve();
}
}
C.Strange Test
C题链接
题意:给两个数a,b,有以下三种操作,每个操作都可以做任意次,问最少几次操作可以使a和b相等
1、a = a + 1
2、b = b + 1
3、a = a | b
思路: 首先容易看出,最后使得a和b相等的操作,要不就是第一种,要不就是第三种如果是第一种的话,答案就是b - a,如果是第三种的话,那么最后一步之前一定存在a | b = b。
因此我们一开始用b-a作为我们的初始值,之后我们用枚举对比,当到达k之后,k|a=b,此时让b-a与k-a+1进行比较即可,然后再特判一下i|a=i的i,此时i必须大于b,我们先让b加到i,再和a或得到i,这个地方i必须是大于b切满足i|a=i的最大整数,再和i-b+1比较。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve()
{
int a,b;
cin>>a>>b;
int res=b-a;//第一种情况
for (int i = a; i <= b; i ++ ) {
if ((i | b) == b) {
res = min(res, i - a + 1);//直接或得到
break;
}
}
for (int i = b; i>0; i ++ ) {
if ((i | a) == i) {
res = min(res, i - b + 1);//改变b值后或运算得到
break;
}
}
cout << res << endl;
}
int main()
{
int t;
cin>>t;
while(t--)
{
solve();
}
}
D.New Year Concert
D题链接
题意:给定一个长度为n的数组a,我们定义一个数组是bad,当且仅当存在这么一对 [l,r][l,r] 使得 gcd(al,al+1,…,ar)=r−l+1gcd(al,al+1,…,ar)=r−l+1。
我们可以通过改变数组中的数来避免这一现象。
定义 f(a)f(a) 为对于长度为k的数组中,至少要改变多少的数使得不存在bad数组。
对于一个长度为n的数组a,求出 f(a1),f(a1,a2),…,f(a1,a2,…,an)
思路:对于一段连续的数来说,gcd是单调递减的,数组长度是单调递增的。所以最多只存在n个bad数组。所以我们对于每个左边界 ll,最多能找到一个 rr 满足bad数组的定义。以上方法可以通过二分查找出。那么查到以后,我们的想法是直接修改成一个大质数即可,由于原数组值域只有1e9,我们修改为1e9 + 7由此可知,我们需要用到单点修改,区间gcd查询,二分,用线段树即可实现
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200000, LN = 20;
int f[N][LN];
int n;
int a[N];
int gcd(int a, int b)// dddd
{
if(b == 0) return a;
else return gcd(b, a % b);
}
void init() //st预处理所有区间的的gcd
{
for(int st = 0; (1 << st) <= n; st ++)
for(int i = 0; i <= n - (1 << st); i ++)
{
if(st == 0)
{
f[i][st] = a[i];
continue;
}
f[i][st] = gcd(f[i][st - 1], f[i + (1 << (st - 1))][st - 1]);
}
}
int getgcd(int l, int r) //st的查询函数
{
int st = __lg(r - l + 1); //找到最高位位数的__lg函数
return gcd(f[l][st], f[r - (1 << st) + 1][st]);
}
int main()
{
cin >> n;
for(int i = 0; i < n; i ++) cin >> a[i];
init();
int maxr = -1;
int be = 0;
int res = 0;
//区间贪心,找到最小的r并让所有包含r的区间(for a fixed l, as r increaes, gcd never changes;you should use less operates to make value as a
// as great as possible, so by iterating ,you can obtain a r(max), which getgcd(l, r) >= r - l + 1)全部砍掉(a[r] == a big prime)
for(int i = 0; i < n; i ++)
{
while(be < i && getgcd(be, i) < i - be + 1) be ++;
if(getgcd(be, i) == i - be + 1)
if(be > maxr)
{
res ++;
maxr = i;
}
cout << res << ' ';
}
return 0;
}