CodeToN cf #3 Div.1+2 (A-D)

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 1bim,(m109)gcd(b1,b2,...,bi)=ai
  • 题解

    • 数论+容斥原理应用。首先先来判断给出的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;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值