【题解】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;
}