Codeforces Round #750 (Div. 2)部分题解(A~D,F1)


前言

「实力」是做题时绝对的必备技能,但「逃课」也是各位必须要掌握的一项技能。
不要误会,博主说的不是那个会让你班主任抓狂被抓到之后会被父母打的三天之内不能下坐的「逃课」

Codeforces Round #750 (Div. 2)部分题解(A~D,F1)_第1张图片
博主说的「逃课」,是指掌握多种编程语言在比赛/做题中灵活切换,以达到高效率高正确率出题的效果「逃课」

博主擅长的语言是C/C++,但如果当博主遇到例如求一个数字的k进制表示的题目。虽然可以通过设计函数实现,十分简单,但是博主是个懒(F)人(W),不想写那么多代码。而恰好在Java语言的Integer类之中,有一个Integer.toString(x, k)的方法,返回 x x x k k k进制的字符串形式。这样一来,博主就省下了不少时间用来思(mo)考(yu)。
既然要「逃课」,那也得先了解一些基础的东西:该语言的基本代码框架比赛中经常出现的多组输入问题等…

下面是Java语言与Python语言的多组输入写法,需要自取。

Java逃课基础代码

import java.util.Scanner;

public class Main {
    public static void main(String[] args){
        Scanner input=new Scanner(System.in);
        while(input.hasNext()){
            /**
             * 主代码
             */
        }
    }
}

关于Scanner.hasNext()实现多组输入的解释:https://blog.csdn.net/qq_41115971/article/details/110820678

Python逃课基础代码

while True:
    try:
       '''
       代码主体
       '''
    except EOFError:
        break

原理解释:
在Python中有一个异常名为EOFError,该异常出现的原因是由于触发了输入文件末尾标志EOF。
因此我们可以利用try...except...检测该异常,从而达到多组输入的效果。


A. Luntik and Concerts(思维)

比赛链接:https://codeforces.com/problemset/problem/1582/A

题目大意

L u n t i k Luntik Luntik打算练习唱歌。
现在有 a a a 1 1 1分钟的歌曲、 b b b 2 2 2分钟的歌曲、 c c c 3 3 3分钟的歌曲供 L u n t i k Luntik Luntik练习。 L u n t i k Luntik Luntik打算在上午练习一部分歌曲,下午再把剩下的歌曲练习完。

L u n t i k Luntik Luntik希望他上午的练习时间与下午的练习时间相差不大,他想知道这个练习时差最小可以是多少。

思路

十分简单的一道思维题。

首先,我们先把每首歌的持续时间都减少 2 2 2分钟(这对结果不会产生影响)。
那么题目就变成了: a a a − 1 -1 1分钟的歌曲、 b b b 0 0 0分钟的歌曲、 c c c 1 1 1分钟的歌曲供 L u n t i k Luntik Luntik练习。
0 0 0分钟的歌曲怎么分都无所谓,所以直接忽略掉。
我们来看 − 1 -1 1分钟的歌曲与 1 1 1分钟的歌曲对结果产生的影响。

我们假设上午的练习时间为 x = 0 x=0 x=0,下午的练习时间为 y = 0 y=0 y=0

  • 我们把一首 − 1 -1 1分钟的歌曲分给上午,此时 x = − 1 x=-1 x=1 y = 0 y=0 y=0,时间之差为 1 1 1
  • 我们把一首 1 1 1分钟的歌曲分给上午,此时 x = 1 x=1 x=1 y = 0 y=0 y=0,时间之差为 1 1 1

实际上,我们把一首 − 1 -1 1分钟的歌曲分给上午,其实是变相地把一首 1 1 1分钟的歌曲分给下午。

Codeforces Round #750 (Div. 2)部分题解(A~D,F1)_第2张图片

两种操作可以视为一种操作:给上午/下午增加一分钟的练习时间。

那么问题就变成:判断(a+c)的奇偶性。

AC代码

#include
using namespace std;
typedef long long ll;

int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        ll a,b,c;
        cin>>a>>b>>c;
        cout<<(a+c)%2<<endl;
    }
}

B - Luntik and Subsequences(思维+排列组合+位运算+快速幂)

比赛链接:https://codeforces.com/problemset/problem/1582/B

题目大意

现给出长度为 n n n的序列 a a a,序列 a a a的元素之和记为 s s s
请求出:在序列 a a a中有多少个不同的子序列(包括空序列)满足序列和 s ′ = s − 1 s'=s-1 s=s1

思路

很简单,找1的数量就可以了。
一开始我是这么想的,但后来发现输入数据中有 0 0 0的存在。

那么子序列的个数就不再只是 1 1 1的个数,还要考虑到对 0 0 0的排列组合。
由于序列中也包含空序列,所以答案应该是:
c n t 1 cnt_1 cnt1 * C c n t 0 0 C_{cnt_0}^{0} Ccnt00 * C c n t 0 0 C_{cnt_0}^{0} Ccnt00* … * C c n t 0 c n t 0 C_{cnt_0}^{cnt_0} Ccnt0cnt0
其中 c n t 1 cnt_1 cnt1代表数字 1 1 1出现的次数, c n t 0 cnt_0 cnt0代表数字 0 0 0出现的次数。

c n t 1 cnt_1 cnt1后面的值实际上就是 2 c n t 0 2^{cnt_0} 2cnt0,所以求起来也是十分简单。

AC代码(位运算求 2 n 2^n 2n)

#include
using namespace std;
typedef long long ll;

int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        ll cnt0=0,cnt1=0;
        int n;
        cin>>n;
        for(int i=0;i<n;i++){
            int x;
            cin>>x;
            if(x==1) cnt1++;
            else if(x==0) cnt0++;
        }
        //1<
        cout<<cnt1*(1LL<<cnt0)<<endl;   
    }
}

AC代码(快速幂求 2 n 2^n 2n)

#include
using namespace std;
typedef long long ll;

//快速幂函数
ll quickpower(ll base,ll power)
{
    ll result=1;
    while(power){
        if(power&1) result*=base;
        power>>=1;
        base*=base;
    }
    return result;
}

int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        ll cnt0=0,cnt1=0;
        int n;
        cin>>n;
        for(int i=0;i<n;i++){
            int x;
            cin>>x;
            if(x==1) cnt1++;
            else if(x==0) cnt0++;
        }
        cout<<cnt1*quickpower(2,cnt0)<<endl;
    }
}

C - Grandma Capa Knits a Scarf(枚举+模拟+回文)

比赛链接:https://codeforces.com/problemset/problem/1582/C

题目大意

现在给出一个字符串 a a a
你可以选择一个字符 c c c,把字符串 a a a中部分字符 c c c删掉,使得处理之后的 a ′ a' a是一个回文串。

问:最少需要删除多少个字符?

思路

首先先说一下如何解决字符串s中的最长回文子序列问题:
将字符串 s s s倒置获得新字符串 s ’ s’ s,求 s s s s ′ s' s最长公共子序列 s ′ ′ s'' s s ′ ′ s'' s即为 s s s的最长回文子序列。

但这道题和上面说的没有太大关系。
我们只需要枚举被删掉的字符 c c c,模拟形成回文串的过程即可,双层for循环但有一层只有 26 26 26次而已。

AC代码

#include
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;

int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        string ss;
        int n,ans=inf;
        cin>>n;
        cin>>ss;
        for(int i=0;i<26;i++){
            int l=0,r=n-1;
            int flag=1;
            int cnt=0;
            while(l<r){
                if(ss[l]!=ss[r]){
                    if(ss[l]=='a'+i)
                        l++,cnt++;
                    else if(ss[r]=='a'+i)
                        r--,cnt++;
                    else{
                        flag=0;
                        break;
                    }
                }
                else l++,r--;
            }
            if(flag)
                ans=min(cnt,ans);
        }
        if(ans==inf) cout<<"-1"<<endl;
        else cout<<ans<<endl;
    }
}

D - Vupsen, Pupsen and 0(思维+数学)

比赛链接:https://codeforces.com/problemset/problem/1582/D

题目大意

V u p s e n Vupsen Vupsen不喜欢 0 0 0的虚无感,他现在给 P u p s e n Pupsen Pupsen一个无 0 0 0数组。
Pupsen十分喜欢 0 0 0 0 0 0有一种圆润的可爱,但他又不希望自己最好的朋友伤心。

于是他向你寻求帮助,能否找到一个数组b,使得:

  1. b b b数组中没有 0 0 0
  2. ∑ i = 1 n ( a i ∗ b i ) = 0 \sum_{i=1}^n {(a_i*b_i)}=0 i=1n(aibi)=0

思路

简单思维题。
可以把 a a a数组的数想象成自爆卡车兵。如果 n n n为偶数,则我们把他们分成 n / 2 n/2 n/2组,两两自爆:

/*
a:  1 -1  3  2
b: -1 -1  2 -3

(1*(-1)+(-1)*(-1))+(3*2+2*(-3))=(0)+(0)=0
*/
for(int i=1; i<=n; i++)
{
    if(i!=1) cout<<" ";
    if(i&1) cout<<arr[i+1];
    else cout<<-arr[i-1];
}

如果 n n n为奇数,其实就是在原有的n/2组中,让 a 1 a_1 a1承担处理 a 2 a_2 a2 a n a_n an,剩下的正常处理。
需要注意 b 1 b_1 b1不能为 0 0 0

AC代码

#include
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+100;
typedef long long ll;

ll arr[maxn];

int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
            cin>>arr[i];
        if(n&1){
            for(int i=1;i<=n;i++){
                if(i!=1) cout<<" ";
                if(i==1){
                    if(arr[i+1]+arr[n]!=0)     //防止b[1]==0
                        cout<<arr[i+1]+arr[n];
                    else
                        cout<<arr[i+1]-arr[n];
                }
                else if(i==n){
                    if(arr[2]+arr[n]!=0)
                        cout<<-arr[1];
                    else
                        cout<<arr[1];
                }
                else if(i&1) cout<<arr[i+1];
                else cout<<-arr[i-1];
            }
            cout<<endl;
        }
        else{
            for(int i=1;i<=n;i++){
                if(i!=1) cout<<" ";
                if(i&1) cout<<arr[i+1];
                else cout<<-arr[i-1];
            }
            cout<<endl;
        }
    }
}

F1 - Korney Korneevich and XOR (easy version) (动态规划+贪心)

比赛链接:https://codeforces.com/problemset/problem/1582/F1

题目大意

现在给出一个长度为 n n n的序列 a a a
输出所有由序列 a a a的子序列 a ′ a' a产生异或和的值的个数,并从小到大输出每个值。
子序列 a ′ a' a需要是一个严格递增序列。

思路

异或有一个特性:
n n n个数异或和之后得到的值 t m p tmp tmp,其最大值是第一个大于 n n n个数中最大的数的2的幂次。

a i a_i ai的最大值为 500 500 500,所以这道题最后求出的所有值中最大只能是 512 512 512
由此我们就可以做动态规划,思想类似于求最长上升子序列,创建数组 a a a,含义如下所示。

ans[i]=x    ==>   异或和为i的序列的最后一个数字为x

对于每次输入的 x x x,首先先更新 a n s ( x ) ans(x) ans(x)的值。
但由于 存在在 x x x输入之前已存在的序列中已有满足条件的子序列 a ′ a' a的异或和为 x x x 的情况,需要进行判断:ans[x]=min(ans[x],x)

接下来就是遍历ans数组,看x是否可以与之前的序列产生新的序列和,或是减小某个序列和的下限。

for(int j=1; j<=512; j++)
{
    if(x>ans[j]) ans[x^j]=min(x,ans[x^j]);
}

AC代码

#include
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+100;
typedef long long ll;

///异或性质:最大值为大于500的第一个2的阶乘
int ans[550];

int main()
{
    ios::sync_with_stdio(false);
    int n,x;
    cin>>n;
    memset(ans,inf,sizeof(ans));
    ans[0]=0;
    for(int i=1;i<=n;i++){
        cin>>x;
        ans[x]=min(ans[x],x);
        for(int j=1;j<=512;j++){
            if(x>ans[j]) ans[x^j]=min(x,ans[x^j]);
        }
    }
    int cnt=0;
    for(int i=0;i<=512;i++)
        if(ans[i]!=inf) cnt++;
    cout<<cnt<<endl;
    for(int i=0;i<=512;){
        while(ans[i]==inf)i++;
        cout<<i;
        i++;
        cnt--;
        if(cnt!=0) cout<<" ";
        if(cnt==0) break;
    }
    cout<<endl;
}

后话

感谢阅读,希望能对你产生一点用处。
以下台词取自《银魂》第70集(小玉篇):

Codeforces Round #750 (Div. 2)部分题解(A~D,F1)_第3张图片

"保护不了应该保护的东西而苟活于世,对武士而言就等同于死。"
"如果只有5%的生存几率的话,那就用这5%的几率来保护你。"
"一旦决定要保护的东西,无论遇到什么事都要保护到底,这才是武士。"
"请你把它加到数据库里去,放在比勇者和魔王更高的位置上。"

吾日三省吾身:日更否?刷题否?快乐否?
更新了,但不是日更;已刷;平静
路漫漫其修远兮,吾将上下而求索

Codeforces Round #750 (Div. 2)部分题解(A~D,F1)_第4张图片

你可能感兴趣的:(CF,题解,acm竞赛,算法,动态规划)