大家好,因为小编水平限制,只会写出div2前四题的题解
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题链接
题意:题意:给定一个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题链接
题意:给两个数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题链接
题意:给定一个长度为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; }