Codeforces Contest #638 Div.2

A. Phoenix and Balance

题目链接

题目大意

有$n$个硬币有着$2^1,2^2,\cdots,2^n$的重量,是偶数。
把硬币分成数量相同的两堆,使得两堆的重量相差最小

解题思路

根据等比数列的求和公式$s_n=\frac{2(1-2^n)}{1-2}=2^{n+1}-1$。
所以我们的得出一个质量为$2^n$的硬币的那堆比它小的所有硬币相加的质量还要大$1$。
为了使两堆的相差最小,根据贪心,我们很容易得到可以分成这样的两堆其中一堆是$2^1,2^2,2^{n/2-1},2^n$。

源码

#include <bits/stdc++.h>
#define DEBUG puts("Here is a BUG")

#define PI 3.1415926535897932626
#define all(a) a.begin(),a.end()
typedef long long ll;
using namespace std;
const double eps=1e-8;
const int MAXN=(int)1e5+5;
const int MOD=(int)1e9+7;
const int INF=0x3f3f3f3f;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;
    cin>>T;
    while (T--)
    {
        ll a,b;
        a=b=0;
        int n;
        cin>>n;
        for(int i=1;i<n/2;i++)a+=(1ll<<i);
        a+=(1ll<<n);
        for(int i=n/2;i<n;i++)b+=(1ll<<i);
        cout<<a-b<<endl;
    }
    return 0;
}

B. Phoenix and Beauty

题目链接

题目大意

有一个长度为$n$的排列,我们希望添加一些数字使得它所有大小为$k$的子序列的和相同。

解题思路

我们先考虑无法构造的情况,就是该序列中的数值的种类个数大于k
然后我们考虑构造一个循环串,所以我先尝试了一下将数字按照出现的顺序依次排列作为循环串的循环节,如果循环节的长度小于$k$那么在循环节的末尾补数值$k$,使得长度为$k$,然后循环的次数为$n$。结果一发 WA估计是构造的太长了。
接下来考虑优化,我们发现不一定循环节的长度需要构造到k,长度只需要是$k$的因数就好了。

源码

#include <bits/stdc++.h>
#define DEBUG puts("Here is a BUG")

#define PI 3.1415926535897932626
#define all(a) a.begin(),a.end()
typedef long long ll;
using namespace std;
const double eps=1e-8;
const int MAXN=(int)1e5+5;
const int MOD=(int)1e9+7;
const int INF=0x3f3f3f3f;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
int a[MAXN];
map<int,int>mp;
set<int> s;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T,n,k;
    cin>>T;
    while (T--)
    {
        cin>>n>>k;
        s.clear(),mp.clear();
        int tmp,cur=0;
        memset(a,0,sizeof(a));
        for (int i = 0; i < n; i++)
        {
            cin>>tmp;
            if(!s.count(tmp))s.insert(tmp),mp[cur]=tmp,cur++;
        }
        if(cur>k){cout<<-1<<endl;continue;}
    	while(k%cur>0)mp[cur++]=k;
        cout<<n*cur<<endl;
        for (int i = 0; i < n; i++)for (int j = 0; j < cur; j++)cout<<mp[j]<<" ";
        cout<<endl;
    }
    return 0;
}

C. Phoenix and Distribution

题目链接

题目大意

将字符串$s$拆分成$k$个字符串,这些字符串不一定要是$s$的子串,求出在所有分出的字符串中的最大字典序的字符串的字典序最小。

解题思路

这题可以分成三种情况

  • 最小的字母的数量小于k,那么答案就是单个字符,就是整个字符串按照排序后的第k个字符
  • 最小的字母的数量等于k且字母的种类只有两个或者一个只有一个字母,那么就把所有的字母的数量平分就好了
  • 其余的情况就是整个字符串排序好后的从第k个字符开始的子串

源码

#include <bits/stdc++.h>
#define DEBUG puts("Here is a BUG")

#define PI 3.1415926535897932626
#define all(a) a.begin(),a.end()
typedef long long ll;
using namespace std;
const double eps=1e-8;
const int MAXN=(int)1e5+5;
const int MOD=(int)1e9+7;
const int INF=0x3f3f3f3f;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    char buf[MAXN];
    int n,k,T;
    int a[26];
    cin>>T;
    while (T--)
    {
        cin>>n>>k;
        cin>>buf;
        memset(a,0,sizeof(a));
        for (int i = 0; i < n; i++)a[buf[i]-'a']++;
        sort(buf,buf+n);

        //第一种
        if (a[buf[0]-'a']<k)
        {
            int cur=a[buf[0]-'a'];
            cout<<buf[k-1]<<endl;
            continue;
        }

        //第二种
        int m=0;
        for (int i=0;i<26;i++)if(a[i])m++;
        if(m==1)
        {
            cout<<string((n+k-1)/k,buf[0])<<endl;
            continue;
        }
        if(m==2&&a[buf[0]-'a']==k)
        {
            cout<<buf[0];
            for (int i = 0; i < n; i++)if(buf[0]!=buf[i]){cout<<string((a[buf[i]-'a']+k-1)/k,buf[i])<<endl;break;}
            continue;
        }

        //第三种
        cout<<buf+k-1<<endl;
    }
    
    return 0;
}

D. Phoenix and Science

题目链接

题目大意

初始的时候你有1个质量为1的细菌。
每天细菌都可以分裂成两块,然后每天晚上每个细菌的重量都会+1。
给定一个细菌质量,求所需要的最小天数和每天分裂的情况

解题思路

设$a_i$为第$i$天的最大质量,$b_i$为第$i$的细菌最多的数量。
我们可以得出如下的递推式

$$
a_i=a_{i-1}+2b_{i-1}\
b_i=2b_{i-1}
$$

然后我们可以根据二分查找找到所需要的最少天数$k$

然后接下来考虑分裂方案,我们分为两种情况来考虑

  • 就算第$k$天不分裂,生成的质量还是大于$n$。设第$k-1$天的分裂的数量为$m$,第$k$天的分裂数量为$t$,我们可以列出一下方程$n=a_{k-2}+(b_{k-2}+m)+(b_{k-2}+m+t)$,这里我们考虑一种特殊解 $m=\lfloor\frac{n-a_{k-2}}{2}\rfloor-b_{k-2}$,若$n-a_{k-2}$为奇数那么$t=1$否者$t=0$。
  • 第$k$天的分裂数量为$n-a_{k-1}-b_{k-1}$

源码

#include <bits/stdc++.h>
#define DEBUG puts("Here is a BUG")

#define PI 3.1415926535897932626
#define all(a) a.begin(),a.end()
typedef long long ll;
using namespace std;
const double eps=1e-8;
const int MAXN=(int)1e5+5;
const int MOD=(int)1e9+7;
const int INF=0x3f3f3f3f;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
ll a[2000],an[2000];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int cur=0;
    a[0]=1,an[0]=1;
    while (a[cur]<(ll)1e9)
    {
        cur++;
        a[cur]=a[cur-1]+an[cur-1]*2;
        an[cur]=an[cur-1]*2;
    }
    int T,n;
    cin>>T;
    while (T--)
    {
        cin>>n;
        int ans=lower_bound(a+1,a+cur,n)-a;
        cout<<ans<<endl;
        int x,y;
        if(ans==1)
        {
            if(n==2)cout<<0<<endl;else cout<<1<<endl;
            continue;
        }
        if(a[ans-1]+an[ans-1]>=n)
        {
        	ll k=n-a[ans-2];
            if(k&1)y=1;
            else y=0;
            x=k/2-an[ans-2];
            for (int i = 1; i <= ans-2; i++)cout<<an[i-1]<<" ";
            cout<<x<<" "<<y<<endl;
		}else
        {
            for (int i = 1; i <= ans-1; i++)cout<<an[i-1]<<" ";
            cout<<(n-a[ans-1]-an[ans-1])<<endl;
        }
        
    }
    
    return 0;
}

比赛总结

B题卡了两次,心态有点崩,D题踩点交结果WA,原因出来第二种情况最后的一个写错了,┭┮﹏┭┮

EricXia

EricXia

喜欢睡觉,热爱钻研各种问题。