CodeToN cf #3 Div.1+2
文章目录
A. Indirect Sort
-
题意
- 给定一个n的排列,问这个排列能否通过下列操作变成单调不递减的数组
- 操作:从前到后选择三个不同的位置i,j,k,如果ai>ak,那么ai=ai+aj,否则swqp(aj,ak)
-
题解
- 思维+贪心。思路来源:一个是ai>ak也可以叠加,另一个是最后的数组需要单调不减,那么就会容易想到最极端的数据1
- 对于数值1,模拟一下情况,看可以怎样操作。充分
- 如果i指向1,由于1是最小的ai<ak恒成立,所以1=ai不可能增大,所以1会一直存在于数组中。所以1要么本来就在第一个位置,要么可以通过操作把1从其他位置移动到第一个位置
- 那么k指向1的时候,ai>ak恒成立,所以1的位置不变;当j指向1时,1要么不变,要么会向后移动,即移动不到第一个位置
- 由此得出结论,1不在第一个位置一定不可以,逆否则为只有1在第一个位置可以
- 证明结论的必要性,如果1在第一个位置,可以让i一直指向1,那么ai<ak恒成立,即j,k的位置可以互换,相当于1之外的位置都可以两两互换,那么一定可以得到一个单调不递减的数组。必要性证明完毕
-
代码
#include <iostream>
using namespace std;
void solve() {
int n,a[15]; cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
if(a[0]==1) cout<<"Yes"<<'\n';
else cout<<"No"<<'\n';
}
int main() {
int t=1; cin>>t;
while(t--) solve();
return 0;
}
B. Maximum Substring
-
题意
-
给定一个长度为n的01串,对于任意一个子串的价值计算如下
v a l u e = { c n t 0 × c n t 1 , ( c n t 0 ! = 0 , c n t 1 ! = 0 ) c n t 0 × c n t 0 , ( c n t 0 ! = 0 , c n t 1 = 0 ) c n t 1 × c n t 1 , ( c n t 0 = 0 , c n t 1 ! = 0 ) value= \begin{cases} cnt_0\times cnt_1,&(cnt_0!=0,cnt_1!=0)\\ cnt_0\times cnt_0,&(cnt_0!=0,cnt_1=0)\\ cnt_1\times cnt_1,&(cnt_0=0,cnt_1!=0)\\ \end{cases} value=⎩ ⎨ ⎧cnt0×cnt1,cnt0×cnt0,cnt1×cnt1,(cnt0!=0,cnt1!=0)(cnt0!=0,cnt1=0)(cnt0=0,cnt1!=0) -
问所有子串价值的最大值为多少
-
-
题解
- 贪心。如果子串选择有0有1的,那么肯定整个字符串是最大值;如果子串只有0或者1时,那么含有最大连续相同字符的子串价值最大。所以只需要比较两种价值哪一种更大
-
代码
#include <iostream>
using namespace std;
const int N=1e5+10;
int a[N];
void solve() {
int n; long long res=0; cin>>n;
string s; cin>>s;
int ma=0,maa=0,cnt1=0,cnt0=0;
int temp=0;
for(auto i:s) {
if(i=='1') {
cnt1++;
temp++;
ma=max(ma,temp);
}
else {temp=0;cnt0++;}
}
temp=0;
for(auto i:s) {
if(i=='0') {
temp++;
maa=max(maa,temp);
}
else temp=0;
}
if(1ll*ma*ma>1ll*maa*maa) res=1ll*ma*ma;
else res=1ll*maa*maa;
if(1ll*cnt0*cnt1>res) res=1ll*cnt0*cnt1;
cout<<res<<'\n';
}
int main() {
int t=1; cin>>t;
while(t--) solve();
return 0;
}
C. Complementary XOR
-
题意
- 给定两个长度为n的01串a和b,每次选定一个区间[l,r],a[l,r]进行01翻转,b[1,l-1]和b[r+1,n]进行01翻转
- 问能否使得a和b都变成0,如果能操作的区间应该是哪些
-
题解
- 思维+构造。正着模拟实在是有点摸不着头脑,可以逆向模拟一下,如果a和b都是0,那么通过操作可以变成什么样子,就可以说明只有这种样子可以操作变成0
- 逆向模拟发现:不论区间怎么选,如果进行奇数次操作,那么ai=!bi(对于每个i来说);如果进行偶数次操作,那么ai=bi(对于每个i来说)。因此可以得出结论,只有a=b,或者a与b完全相反时可以变成0
- 如何构造操作区间,其实只需要对a一位一位全变为0,那么此时a都是0,b要么全为1,要么全为0。全为0则不需要操作,全为1则只需操作[1n],[1,1],[2,n]三次即可
-
代码
#include <iostream>
#include <vector>
using namespace std;
typedef pair<int,int> PII;
void solve() {
int n; cin>>n;
string a,b; cin>>a>>b;
if(a!=b) {//不完全相反
bool f=1;
for(int i=0;i<n;i++)
if(a[i]==b[i]) { f=0; break; }
if(!f) {cout<<"NO"<<'\n'; return ;}
}
cout<<"YES"<<'\n';
vector<PII> A;
for(int i=1;i<=n;i++) {
if(a[i-1]=='1') {
A.push_back({i,i});
if(i>1) b[0]=(b[0]=='1' ? '0':'1');
}
}
if(b[0]=='1') { A.push_back({1,n}); A.push_back({1,1}); A.push_back({2,n});}
cout<<A.size()<<'\n';
for(auto x:A) cout<<x.first<<' '<<x.second<<'\n';
}
int main() {
int t=1; cin>>t;
while(t--) solve();
return 0;
}
D. Count GCD
-
题意
- 给定一个长度为n的数组a,问有多少个长度为n的数组b符合下列条件
1 ≤ b i ≤ m , ( m ≤ 1 0 9 ) g c d ( b 1 , b 2 , . . . , b i ) = a i 1\le b_i\le m,(m\le 10^9)\\ gcd(b_1,b_2,...,b_i)=a_i 1≤bi≤m,(m≤109)gcd(b1,b2,...,bi)=ai
- 给定一个长度为n的数组a,问有多少个长度为n的数组b符合下列条件
-
题解
- 数论+容斥原理应用。首先先来判断给出的a数组是否能够构造出合法的b数组。gcd(b1,b2,…,bi-1)=ai-1,gcd(b1,b2,…,bi)=gcd(ai-1, bi)=ai,从这里可以看出a数组之间的关系,即a[i]%a[i-1]=0,由此得到a的合法形式,a[i]%a[i-1]=0对于每一个i
- 如何计算有多少个合法的b数组?对于每一个位置来分析有多少种合法的情况,则b数组的合法数量为n个位置的乘法计数。分析b数组的一个位置:因为gcd(ai-1, bi)=ai,所以gcd(x, y)=1,x,y分别为ai-1,bi对于ai的倍数,即x与y互质,求bi的数量即求y的数量,而x,y的范围为[1, m/a[i]],固bi的数量等价于[1, m/a[i]]的范围内与a[i-1]/a[i]互质的数量。计算方法为容斥原理,参见严格鸽的博客。(简明扼要地说一下,求[1,n]中与k互质的数的个数,只需枚举计算k的质因数排列,通过容斥原理计算数量即可)
- 最后的答案为n个位置的乘法原理
-
代码
#include <iostream>
#include <vector>
using namespace std;
const int N=2e5+10,mod=998244353;
typedef long long ll;
int n,m,a[N];
int num,sum;
vector<int> primes;
void prime(int x) {//分解质因数
primes.clear();
for(int i=2;i*i<=x;i++)
if(x%i==0) {
primes.push_back(i);
while(x%i==0) x/=i;
}
if(x>1) primes.push_back(x);
}
void dfs(int pos,int mul,int len) {//搜索枚举每一种质因数的选取情况,用容斥原理计算数量
if(pos==primes.size()) {
if(len) {
if(len%2) sum+=num/mul;
else sum-=num/mul;
}
return ;
}
dfs(pos+1,mul,len);
dfs(pos+1,mul*primes[pos],len+1);
}
int cal(int l,int k) {//计算[1,L]中与k互质的数量
prime(k);
num=l,sum=0;
dfs(0,1,0);
return l-sum;
}
int solve() {
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=2;i<=n;i++)//a数组不合法
if(a[i-1]%a[i]) return 0;
ll res=1;
for(int i=2;i<=n;i++) {
if(a[i]==a[i-1]) res=res*(m/a[i])%mod;//降低复杂度
else res=res*cal(m/a[i],a[i-1]/a[i])%mod;//计算每一个位置的合法数量
}
return res;
}
int main() {
int t; cin>>t;
while(t--) cout<<solve()<<'\n';
return 0;
}