【题解】Educational Codeforces Round 83(CF1312)

A.Two Regular Polygons

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

点击查看代码
#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 从大到小排序。
考虑原式的变形:

jiajai

这样就可以使得 ji 一定为正数,而 ajai 是一个小于等于 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

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

点击查看代码
#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 个数里选出 n1 个数,使得他们分布在最大值的左边或右边,并且有一个数是左右都有的,也就是重复的,所以答案就是

2n3×(mn1)×(n2)

第一项:选左边或右边。第二项:选 n1 个数。第三项:选哪个数重复。

点击查看代码
#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

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

dp[l][r]=minmid=lr1(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 @   linyihdfj  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示