cf #829 Div.2(A~D)

Cf #829 Div.2

A. Technical Support

  • 题意

    • 给定一个以Q开头的只含有’Q’&'A’的字符串,每一个问题后面必须有回答
    • 问给定的字符串是否符合要求
  • 题解

    • 贪心,Q后面必须有A,那么直接倒着遍历,用计数器cnt来记录A的数量,如果出现了cnt为负数,即某个Q没有回答,那么不符合要求
  • 代码

#include <iostream>

using namespace std;

bool solve() {
    int n,a=0; cin>>n;
    string s;
    cin>>s;
    
    for(int i=n-1;i>=0;i--) {
        if(s[i]=='A') a++;
        else a--;
        if(a<0) return 0;
    }
    return 1;
}

int main() {
    int t; cin>>t;
    while(t--) cout<<(solve() ? "Yes":"No")<<'\n';
    
    return 0;
}

B. Kevin and Permutation

  • 题意

    • 构造一个n的排列,使得所有两两相邻的元素的差值的最小值最大,即使得下列式子最大
      m i n i = 1 i = n ∣ p i − p i − 1 ∣ min_{i=1}^{i=n}|p_i-p_{i-1}| mini=1i=npipi1
  • 题解

    • 贪心+构造,首位思考所有的差值如何保证最大,如果能保证所有两个数的差值都相等(平均不等式),那么此差值最大。
    • 差值是两两之间的事,我们先以2个数化成一块来构造,跨过中间数区组成一个块可以保证最大,即1和n/2+1构成一个块,后面的数同理,n个数可以构造成n/2块,块内的差值为n/2。
    • 块间的数如果顺着放那么块之间逆着放,即含有大的数的块放前面,这样可以保证块之间的差值不会导致最小差值变小。相反的,如果块内的数逆着放那么块就顺着放
  • 代码

//块内正着放
#include <iostream>

using namespace std;

int solve() {
    int n,cnt=0;
    cin>>n;
    int x=n/2;
    for(int i=0;i<x;i++) 
        cout<<n-i-x<<' '<<n-i<<' ';
    if(n&1) cout<<1;//注意奇数时,1当读成块
    puts("");
}

int main() {
    int t; cin>>t;
    while(t--) solve();
    
    return 0;
}


//块内逆着放
#include <iostream>

using namespace std;

void solve() {
    int n; cin>>n;
    int x=n/2;
    for(int i=1;i<=x;i++) 
        cout<<i+x<<' '<<i<<' ';
    
    if(n&1) cout<<n;
    puts("");
}

int main() {
    int t; cin>>t;
    while(t--) solve();
    
    return 0;
}

C1. Make Nonzero Sum (easy version)

  • 题意

    • 给定一个长度为n的只含有-1和1的数组a,可以将数组分成k个区间,每个区间的贡献为sum[ (-1)^(区间的第几个数,从0开始)*对应的数值 ]
    • 求能使得k个区间的和为0的区间划分情况
  • 题解

    • 数学+思维,若数组长度为奇数必然无法通过划分区间使得答案为0,证明:现将所有的数都看成一个区间,那么此时的答案即为数组的和,且此时答案必然不为零且为奇数;考虑区划分区间使得答案为0,如果区间合并,那么原来是-1变为1,原来是1变为-1,对于答案的变化都是偶数,奇数±偶数依然为奇数,不可能变为0,所以奇数长度的数组不可能有答案,得证
    • 若长度为偶数,那么直接每隔两项看是合并成一个区间,还是直接单独两个区间,这样必然能够构造出答案为0的区间划分。证明:对于某两项来说,如果a[i]=a[i+1]那么直接合并,此时该区间为0;如果a[i]!=a[i+1],那么划分为两个区间si+si+1=a[i]+a[i+1]=0,即这样划分构造一定可以使得此区间对于答案的贡献为0。且由于n为偶数,所以一定可以划分成整数个含两数的区间,使得每隔两项都能有对答案贡献为0,所以最终的答案为0,得证
  • 代码

#include <iostream>
#include <vector>

using namespace std;
const int N=2e5+10;
typedef pair<int,int> PII;

int n,a[N];

void solve() {
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    if(n&1) {cout<<-1<<'\n'; return ;}
    
    vector<PII> ans;
    int sum=0;
    for(int i=1;i<=n;i+=2) {
        if(i==n) {ans.push_back({n,n}); sum+=a[i]; continue;}
        if(a[i]==a[i+1]) ans.push_back({i,i+1});
        else ans.push_back({i,i}),ans.push_back({i+1,i+1});
    }
    
    if(sum) {cout<<-1<<'\n';return ;}
    cout<<ans.size()<<'\n';
    for(auto x:ans) cout<<x.first<<' '<<x.second<<'\n';
}

int main() {
    int t; cin>>t;
    while(t--) solve();
    
    return 0;
}

C2. Make Nonzero Sum (hard version)

  • 题意

    • 给定一个长度为n的只含有-1和1和0的数组a,可以将数组分成k个区间,每个区间的贡献为sum[ (-1)^(区间的第几个数,从0开始)*对应的数值 ]
    • 求能使得k个区间的和为0的区间划分情况
  • 题解

    • 与简单版本一致,如果不为0的位置的数量为奇数,一定无解

    • 不为0的位置数量为偶数,那么也和简单版本一致,把不为0的位置每隔两项组队去看,因为中间0的数量不会给答案做贡献,与简单版本不同是符号±与0有关,要多一点点分析奇偶性,以下用不为0的位置p1,p2进行分析

      1.a[p1]=a[p2]
      	1)p1,p2的奇偶性不同,那么和简单版本一致,区间区间划分为[p1,p2]
      	2)p1,p2的奇偶性相同,有一种构造区间的方式,[p1,p2-2],[p2-1,p2]
      		如	: 1 0 0 |0 1
      
      2.a[p1]!=a[p2],只需要把[p1,p2]的每一个元素都独自划分成一个区间即可,	此时一定有[p1,p2]对答案的贡献为所有元素的和,即为0
      
  • 代码

#include <iostream>
#include <vector>

using namespace std;
const int N=2e5+10;
typedef pair<int,int> PII;

int a[N];

void solve() {
    int n; cin>>n;
    vector<PII> ans;
    vector<int> pos;
    for(int i=1;i<=n;i++) {
        cin>>a[i];
        if(a[i]) pos.push_back(i);
    }
    if(pos.size() & 1) {cout<<-1<<'\n'; return ;}//无解
    
  //注意全为0的状况,pos没有记录,所以需要特判
    if(pos.size()==0){cout<<1<<'\n'<<1<<' '<<n<<'\n'; return ;}
  //与简单版本不同,这里要注意两项与两项之间还有0存在的可能,其间的0只需化为一段即可
    if(pos[0]>1) ans.push_back({1,pos[0]-1});
    for(int i=0;i<pos.size();i+=2) {
   //与简单版本不同,这里要注意两项与两项之间还有0存在的可能,其间的0只需化为一段即可
        if(i && pos[i]>pos[i-1]+1) ans.push_back({pos[i-1]+1,pos[i]-1});
        if(a[pos[i]]==a[pos[i+1]]) {//a[p1]=a[p2]
            if((pos[i]+pos[i+1])%2) ans.push_back({pos[i],pos[i+1]});//p1,p2奇偶不同
            else ans.push_back({pos[i],pos[i+1]-2}),//p1,p2奇偶相同
                ans.push_back({pos[i+1]-1,pos[i+1]});
        }
        else {//a[p1]!=a[p2]
            for(int j=pos[i];j<=pos[i+1];j++) ans.push_back({j,j});
        }
    }
  //注意尾部0也要输出
    if(pos[pos.size()-1]<n) ans.push_back({pos[pos.size()-1]+1,n});
    
    cout<<ans.size()<<'\n';
    for(auto x:ans) cout<<x.first<<' '<<x.second<<'\n';
}

int main() {
    int t; cin>>t;
    while(t--) solve();
    
    return 0;
}

D. Factorial Divisibility

  • 题意

    • 给n个数,以及一个k,问这n个数的阶乘的和是否能整除k的阶乘
  • 题解

    • 思维+数学,因为是看阶乘的和是否能整除,那么对于每一个数的阶乘看成一个整体,同时可以得知数若有x+1个x!,那么可以合成一个(x+1)!。如 3 3 3 2 2 2,其中有3个2!合成一个3!,此时相当于有4个3!所以合成一个4!,若k=4,那么可以整除。
    • 但是若x!不能全部用于合成(x+1)!,那么不能整除,例如 3 3 3 2 2 2 2,有4个2!,合成1个3!的同时,剩余一个2!,最后n个数的阶乘和为4!+2!,此时不能整除k=4
    • 所以思路就是map记录数出现的次数,再验证从1到k-1看是否能依次向上合成,据此判断是否能整除
  • 代码

#include <iostream>
#include <map>

using namespace std;
map<int,int> h;

bool solve() {
    int n,k,x; cin>>n>>k;
    
    for(int i=0;i<n;i++) cin>>x,h[x]++;
    
    for(int i=1;i<k;i++) {
        if(h[i]%(i+1)) return 0;
        h[i+1]+=h[i]/(i+1);
    }
    return 1;
}

int main() {
    
    cout<<(solve() ? "Yes":"No")<<'\n';
    
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
<div id="wea_rich_text_default_font" style="font-family:微软雅黑;font-size:12;"><p><img alt="" src="/weaver/weaver.file.FileDownload?fileid=aaa9aee4717d33272bd7ea028fa03118b693919f23b18febf9f6cee1158e8f4cf027542c71c8cf98d63770ccdf3bd1750e6b92e28c43dccd4" /></p><div class="ckeditor-html5-video" data-widget="html5video" style="text-align:left"><video controls="controls" src="/weaver/weaver.file.FileDownload?fileid=aad6f413f83191673980c5ee24b412880d6b9e8703caca411faec3276fe8133f5fa7e34630ca89ace63770ccdf3bd175071362141037cfb4e&download=1" style="max-width:100%"> </video></div><table border="1" cellpadding="1" style="width:500px;"> <tbody> <tr> <td style="padding: 1px;">1</td> <td style="padding: 1px;">1</td> </tr> <tr> <td style="padding: 1px;">2</td> <td style="padding: 1px;">2</td> </tr> <tr> <td style="padding: 1px;">3</td> <td style="padding: 1px;">3<a href="http://localhost:8080/wui/index.html#/main/portal/portal-1-1?menuIds=0,1&menuPathIds=0,1&_key=zq8830" target="_blank">http://localhost:8080/wui/index.html#/main/portal/portal-1-1?menuIds=0,1&menuPathIds=0,1&_key=zq8830</a></td> </tr> </tbody></table><p>测试<a href="http://localhost:8080/wui/index.html#/main/portal/portal-1-1?menuIds=0,1&menuPathIds=0,1&_key=zq8830" target="_blank">http://localhost:8080/wui/index.html#/main/portal/portal-1-1?menuIds=0,1&menuPathIds=0,1&_key=zq8830</a></p><p> </p><p>修改一下吧 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq<img alt="" src="/weaver/weaver.file.FileDownload?fileid=a7617945ec5f52ec80aaa43ee8504de0a1b14d5eca4a98834494c85349762c626dec7ba8d0da277106ee600d27743f4e44f710fbddd167603" /></p></div>
06-01
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值