【题解】Educational Codeforces Round 83(CF1312)

A.Two Regular Polygons

假设构造多边形的边数为 \(n\),给定多边形的边数为 \(m\),若顶点完全覆盖也就意味着给定的多边形可以被平均分成 \(n\) 份,也就是 \(m\)\(n\) 的倍数

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int main(){
    int t;
    cin>>t;
    while(t--){
        int x,y;
        cin>>x>>y;
        if(x % y == 0)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

B.Bogosort

不好分析所以只能显然了:把 \(a\) 从大到小排序。
考虑原式的变形:

\[j-i \not= a_j - a_i \]

这样就可以使得 \(j-i\) 一定为正数,而 \(a_j - a_i\) 是一个小于等于 \(0\) 的数,所以一定不等。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
int a[MAXN];
int main(){
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=1; i<=n; i++){
            cin>>a[i];
        }
        sort(a+1,a+n+1);
        for(int i=n; i>=1; i--){
            printf("%d ",a[i]);
        }
        printf("\n");
    }
    return 0;
}

C.Adding Powers

看见 \(k^i\) 就联想到 \(k\) 进制,也就是说我们在 \(k\) 进制下每一位只能加一次,那么我们就把 \(a\)\(k\) 进制拆开,如果每一位最多只需要一个那么就合法,否则就不合法。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 100;
int n,k,cnt[MAXN],a[MAXN];
bool flag = true;
void fenjie(int x){
    for(int i=1; i<=64; i++){  //log 也就这么大了
        cnt[i]+=x%k;
        if(cnt[i] >= 2) flag = false;
        x /= k;
    }
    if(x)   flag = false;
}
signed main(){
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    int t;
    cin>>t;
    while(t--){
        memset(cnt,false,sizeof(cnt));
        flag = true;
        cin>>n>>k;
        for(int i=1; i<=n; i++){
            cin>>a[i];
            fenjie(a[i]);
        }
        if(flag)    printf("YES\n");
        else    printf("NO\n");
    }
    return 0;
}

D.Count the Arrays

很明显就是一个推式子的题。
我们要从这 \(m\) 个数里选出 \(n-1\) 个数,使得他们分布在最大值的左边或右边,并且有一个数是左右都有的,也就是重复的,所以答案就是

\[2^{n-3}\times \binom{m}{n-1} \times (n-2) \]

第一项:选左边或右边。第二项:选 \(n-1\) 个数。第三项:选哪个数重复。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MOD = 998244353;
const int MAXN = 2e5+5;
int pre[MAXN];
int power(int a,int b){
    int res = 1;
    while(b){
        if(b & 1)   res = (res * a) % MOD;
        a = (a * a) % MOD;
        b >>= 1;
    }
    return res;
}
signed main(){
    int n,m;
    cin>>n>>m;
    if(n == 2)  printf("0\n");
    else{
        pre[1] = 1;
        for(int i=2; i<=m; i++)
            pre[i] = (pre[i-1] * i)%MOD;
        printf("%lld\n",((((power(2,n-3) * (n-2))%MOD * pre[m])%MOD * power(pre[n-1],MOD-2))%MOD * power(pre[m - n + 1],MOD-2))%MOD);
    }
    return 0;
}

E.Array Shrinking

看到 \(n \le 500\) 以及这种题面显然就可以想到区间 \(\text{DP}\)。状态也非常显然:\(dp[l][r]\) 代表将 \([l,r]\) 最短的合并后的长度。
转移也十分显然:

\[dp[l][r] = \min_{mid = l}^{r-1}(dp[l][mid] + dp[mid+1][r]) \]

需要注意的是:如果两个区间都可以合并为一个数,并且这一个数相同,那么我们就可以合并这两个区间,具体来说就是记 \(w[l][r]\) 表示 \([l,r]\) 如果可以合并为一个数,这个数是谁。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 505;
int dp[MAXN][MAXN],w[MAXN][MAXN],a[MAXN];
int main(){
    memset(dp,0x3f,sizeof(dp));
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    int n;
    cin>>n;
    for(int i=1; i<=n; i++){
        cin>>a[i];
        dp[i][i] = 1;w[i][i] = a[i]; 
    }
    for(int len=2; len<=n; len++){
        for(int l=1; l + len - 1 <= n; l++){
            int r = l + len - 1;
            for(int k=l; k<r; k++){
                dp[l][r] = min(dp[l][r],dp[l][k]+dp[k+1][r]);
                if(w[l][k] == w[k+1][r] && dp[l][k] == 1 && dp[k+1][r] == 1){
                    dp[l][r] = 1;w[l][r] = w[l][k] + 1;
                }
            }
        }
    }
    printf("%d\n",dp[1][n]);
    return 0;
}
posted @ 2022-07-28 07:05  linyihdfj  阅读(22)  评论(0编辑  收藏  举报