Educational Codeforces Round 83 (Rated for Div. 2)


提莫

A - Two Regular Polygons

问题:判断能否通过对正n边形选取m个顶点,形成中心重合且顶点为选取的m个点的正m变形
答案:判断n是否整除m即可,是则YES,否则NO。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    ll t;
    cin>>t;
    while(t--){
    ll n,m;
    cin>>n>>m;
    if(n%m)puts("NO");
    else puts("YES");
    }
}

B - Bogosort

问题:给一个数组a,对其重新排序使得对于任意
i < j i<j i<j满足 j − a j ≠ i − a i j-a_j\not=i-a_i jaj=iai
答案:按从大到小排序即可,使得 i < j i<j i<j a i > a j a_i>a_j ai>aj
就一定可以满足 j − a j > i − a i j-a_j>i-a_i jaj>iai

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[10001];
int main(){
    ll t;
    cin>>t;
    while(t--){
        ll n;
        cin>>n;
        for(ll i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        sort(a+1,a+1+n);
        for(ll i=n;i>=1;i--){
            printf("%lld%c",a[i],i==1?'\n':' ');
        }
    }
}

C - Adding Powers

问题:一个数组v,初始为0,长度为n,给一个底数k
再给一个长为n的目标数组a,问是否可通过如下操作经过若干步将数组v变成数组a。
操作:
每一步有两个选择,设当前步数为i(i从0开始编号)有:
1、选择一个位置pos,将 v p o s v_{pos} vpos加上 k i k^i ki
2、跳过这一步

答案:
从数组a倒推看是否可以减到0数组而不发生矛盾,对每个位置pos的数每次减去可以减去的最大的 k i k^i ki且每个i只能使用一次,即每次都选择使 a p o s − k i ≥ 0 a_{pos}-k^i\geq0 aposki0成立的最大的i使 a p o s a_{pos} apos减去 k i k^i ki,若存在使用的i重复或者数组中某个数不能减到0,则输出NO,否则输出YES。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<int,bool>mp;
int main(){
    int t;
    cin>>t;
    while(t--){
        mp.clear();
        int n,k;
        cin>>n>>k;
        bool flag=true;
        for(int i=1;i<=n;i++){
            ll x;
            scanf("%lld",&x);
            if(!x)continue;
            ll cur=0;
            ll pw=1;
            while(x>=pw*k){
                pw*=k;
                cur++;
            }
            while(cur>=0){
                if(x>=pw){
                    if(mp.count(cur)){
                        flag=false;
                    }
                    else{
                        mp[cur]=true;
                    }
                    x-=pw;
                }
                pw/=k;
                cur--;
            }
            if(x)flag=false;
        }
        if(flag){
            puts("YES");
        }
        else{
            puts("NO");
        }
    }
}

D - Count the Arrays

问题:给两个整数n和m,计算满足以下条件的数列的个数:
1、数列有n个数
2、每个数都属于区间 [ 1 , m ] [1,m] [1,m]
3、数列中有一对相同的数
4、数列存在位置j使[1,j]严格单调递增,[j,m]严格单调递减
答案:组合数学问题,分如下步骤:
1、因数列中有一对重复,从[1,m]中选取n-1个数,方法有 C m n − 1 C_m^{n-1} Cmn1种;
2、n-1个数中有一个重复使用,但不能是n-1中最大的数,因为最大的数若有两个,一定不满足严格单增或单减的条件,有 ( n − 2 ) (n-2) (n2)种取法,将重复的数分别放在最大的数两边;
3、对于剩下的 ( n − 3 ) (n-3) (n3)可按顺序任意选择放在最大的数的左侧还是右侧,有 2 n − 3 2^{n-3} 2n3种方法。
综上, n ≤ 2 n\le2 n2时方法数为0
否则方法数为 C m n − 1 ∗ ( n − 2 ) ∗ 2 n − 3 C_m^{n-1}*(n-2)*2^{n-3} Cmn1(n2)2n3

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=998244353 ;

ll inv(ll a) {
    return a == 1 ? 1 : (ll)(MOD - MOD / a) * inv(MOD % a) % MOD;
}

ll modexp(ll a, ll b)
{
    ll res=1;
    while(b>0)
    {
        if(b&1)
            res=res*a%MOD;
        b=b>>1;
        a=a*a%MOD;
    }
    return res;
}
ll C(ll n,ll m)
{
    if(m < 0)return 0;
    if(n < m)return 0;
    if(m > n-m) m = n-m;

    ll up = 1, down = 1;
    for(ll i = 0 ; i < m ; i ++){
        up = up * (n-i) % MOD;
        down = down * (i+1) % MOD;
    }
    return up * inv(down) % MOD;
}
int main(){
    ll n, m;
    cin>>n>>m;
    if(n==2)cout<<0<<endl;
    else{
        ll ans=C(m,n-1)*(n-2);
        ans%=MOD;
        ans*=modexp(2,n-3);
        ans%=MOD;
        cout<<ans<<endl;
    }
}


E - Array Shrinking

问题:给一个长为n的数列a,可以将两个相邻且相等的数字合并并为原来的数字加1,可重复若干次,问可得到的最短的序列长度时多少。
答案:用 c [ l ] [ r ] c[l][r] c[l][r]表示 [ l , r ] [l,r] [l,r]能否合成一个点, d p [ i ] dp[i] dp[i]表示只考虑前 i i i位可以得到的最短序列长度。
动态规划转移方程是
i f ( c [ i ] [ j ] = = t r u e ) if(c[i][j]==true) if(c[i][j]==true)
d p [ j ] = m i n ( d p [ j ] , d p [ i − 1 ] + 1 ) ; dp[j]=min(dp[j],dp[i-1]+1); dp[j]=min(dp[j],dp[i1]+1);
判断 c [ l ] [ r ] c[l][r] c[l][r]的过程可用栈来优化,去掉一个n,
均摊复杂度 O ( n 2 ) O(n^2) O(n2)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=0x3f3f3f3f;
ll dp[501];
bool c[501][501];
ll a[501];
 
int main(){
    ll n;
    cin>>n;
    memset(dp,INF,sizeof(dp));
    for(ll i=1;i<=n;i++){
        cin>>a[i];
    }
    for(ll i=1;i<=n;i++){
        stack<ll>s;
        for(ll j=i;j<=n;j++){
            ll cur=a[j];
            while(!s.empty()&&s.top()==cur){
                s.pop();
                cur++;
            }
            s.push(cur);
            if(s.size()==1){
                c[i][j]=true;
            }
        }
    }
    dp[0]=0;
    for(ll i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            if(c[i][j]==true)
                dp[j]=min(dp[j],dp[i-1]+1);
        }
    }
    cout<<dp[n]<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值