Educational Codeforces Round 83 (Rated for Div. 2)

A. Two Regular Polygons

 

题意:给两个数m<n,判断能否选择正n边形的m个端点构成正m边形。

思路:构造出的正m边形的每条边对应原正n边形的边的数目是相同的,所以只需要判断n能否被m整除即可。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define dl double
 4 void rd(int &x){
 5  x=0;int f=1;char ch=getchar();
 6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
 8 }
 9 void lrd(LL &x){
10  x=0;int f=1;char ch=getchar();
11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
13 }
14 const int INF=1e9;
15 const LL LINF=1e18;
16 using namespace std;
17 int T,n,m;
18 int main(){
19  rd(T);
20  while(T--){
21   rd(n);rd(m);
22   if(n % m)printf("NO\n");
23   else printf("YES\n");
24  }
25  return 0;
26 }
27 /**/
View Code

反思:记住这个小小的结论吧。

B. Bogosort

题意:给n个数,将它们重新排列使得对于所有i<j,j-aj!=i-ai,输出重新排列后的数组,任意输出一个即可。

思路:从大到小排序即可。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define dl double
 4 void rd(int &x){
 5  x=0;int f=1;char ch=getchar();
 6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
 8 }
 9 void lrd(LL &x){
10  x=0;int f=1;char ch=getchar();
11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
13 }
14 const int INF=1e9;
15 const LL LINF=1e18;
16 const int N=105;
17 using namespace std;
18 int T,n;
19 int a[N];
20 bool cmp(int x,int y){return x>y;}
21 int main(){
22 // freopen("in.txt","r",stdin);
23  rd(T);
24  while(T--){
25   rd(n);
26   for(int i=1;i<=n;i++)rd(a[i]);
27   sort(a+1,a+n+1,cmp);
28   for(int i=1;i<=n;i++)printf("%d ",a[i]);
29   puts("");
30  }
31  return 0;
32 }
33 /**/
View Code

C. Adding Powers

题意:给n个数,问能否通过以下操作将初始全为0的数组变成这n个数。第i次操作可以将一个数加上k^i或者跳过。

思路:将n个数都看成k进制数字,看每一位之和是否都小于等于1即可。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define dl double
 4 void rd(int &x){
 5  x=0;int f=1;char ch=getchar();
 6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
 8 }
 9 void lrd(LL &x){
10  x=0;int f=1;char ch=getchar();
11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
13 }
14 const int INF=1e9;
15 const LL LINF=1e18;
16 const int N=35;
17 using namespace std;
18 int T;
19 int n,k;
20 LL a[N];
21 int main(){
22 // freopen("in.txt","r",stdin);
23  rd(T);
24  while(T--){
25   rd(n);rd(k);
26   for(int i=1;i<=n;i++)lrd(a[i]);
27   bool flg=0;
28   for(int i=0;i<=100;i++){
29    int cnt=0;
30    for(int j=1;j<=n;j++)cnt+=a[j]%k,a[j]/=k;
31    if(cnt > 1)flg=1;
32   }
33   if(flg)printf("NO\n");
34   else printf("YES\n");
35  }
36  return 0;
37 }
38 /**/
View Code

D. Count the Arrays

题意:计算满足以下条件的数组的数目。长度为n(2e5),所有数都在1~m(2e5)(m>=n)之间,存在一个i使得前i个数严格递增,i~n严格递减,有且只有两个数相等。对998244353取模。

思路:组合数计数的问题。考虑已经找到了那两个相等的数字的下标ij,并且他们之间的数字也已经选择好了,那么他们中间的数字的排列方式有C(j-i-2,0)+C(j-i-2,1)+...+C(j-i-2,j-i-2)种,因为最高点的数值是确定的,这个过程是在枚举最高点左面选择哪些数字,因为一旦选择好了排列方式是唯一的。共计2^(j-i-2)种。而对于ij两边的数字,也是类似的情况,只需要枚举左面选的那些即可,共计2^(n-j+i-1)种,也就是说,在ij确定,他们之间和两边的数字都确定的情况下,共有2^(n-3)种排列方式,与ij的具体值无关。而我们只需要从m个数字种选出n-1个数,再选定相等的两个数字之后,ij之间和两边的数字集合其实也是确定了的,而根据上面的计算共计2^(n-3)种,同时不能选取最大值作为相等的两数字,也就是说最终的方案数为C(m,n-1)*(n-2)*2^(n-3)。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define dl double
 4 void rd(int &x){
 5  x=0;int f=1;char ch=getchar();
 6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
 8 }
 9 void lrd(LL &x){
10  x=0;int f=1;char ch=getchar();
11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
13 }
14 const int INF=1e9;
15 const LL LINF=1e18;
16 const int N=2e5+10;
17 const int mod=998244353;
18 using namespace std;
19 int n,m;
20 int fac[N],pw[N];
21 int fast_pow(int x,int y){
22  int ans=1;
23  while(y){
24   if(y&1)ans=1ll*ans*x%mod;
25   x=1ll*x*x%mod;y>>=1;
26  }
27  return ans;
28 }
29 int main(){
30 // freopen("in.txt","r",stdin);
31  rd(n);rd(m);fac[0]=1;pw[0]=1;
32  for(int i=1;i<=max(n,m);i++)fac[i]=1ll*fac[i-1]*i%mod,pw[i]=pw[i-1]*2%mod;
33  if(n == 2)printf("0\n");
34  else {
35   int ans=1ll*fast_pow(fac[n-1],mod-2)*fast_pow(fac[m-n+1],mod-2)%mod*fac[m]%mod*(n-2)%mod*pw[n-3]%mod;
36   printf("%d\n",ans);
37  }
38  return 0;
39 }
40 /**/
View Code

反思:我在推出2^(n-3)之后一直没有进展,因为我一直觉得要在m-1个数字中选出ij之间的数字,而这又和ij的具体位置有关了,再选择出两边的数字,得到的式子根本无法化简,而事实上完全不需要那么复杂。可能这就是组合数的魅力吧。

E. Array Shrinking

题意:n(500)个数字,每次可以将相邻的两个相等数字合并为一个数字,并且这个数字的数值等于原数字+1,求最终这个序列最短是多少。

思路:求最短,dp,500的范围,区间dp。基本确定算法后考虑怎么实现。自然而然想到f[i][j]表示i~j最短多短,最终答案就是f[1][n],考虑如何转移。直接枚举中间的k进行转移,那么对于合并的情况如何考虑呢?我们发现如果要合并,合并的两个区间的f一定是1,不然如果可以合并的话,他们的子区间也一定可以进行合并,所以记录一下f为1的区间当前的值为多少进而进行f为1的区间之间的合并即可。因为区间dp是先对长度小的区间dp,所以可以保证处理ij时他们区间内可以合并的情况已经全部计算在内了。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define dl double
 4 void rd(int &x){
 5  x=0;int f=1;char ch=getchar();
 6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
 8 }
 9 void lrd(LL &x){
10  x=0;int f=1;char ch=getchar();
11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
13 }
14 const int INF=1e9;
15 const LL LINF=1e18;
16 const int N=505;
17 int n,a[N];
18 int f1[N][N],f2[N][N]; 
19 using namespace std;
20 int main(){
21 // freopen("in.txt","r",stdin);
22  rd(n);for(int i=1;i<=n;i++)rd(a[i]),f1[i][i]=1,f2[i][i]=a[i];
23  for(int l=2;l<=n;l++){
24   for(int i=1;i+l-1<=n;i++){
25    int j=i+l-1;f1[i][j]=0x7fffffff;
26    for(int k=i;k<=j;k++){
27     f1[i][j]=min(f1[i][j],f1[i][k]+f1[k+1][j]);
28     if(f1[i][k] == 1 && f1[k+1][j] == 1 && f2[i][k] == f2[k+1][j])f1[i][j]=1,f2[i][j]=f2[i][k]+1;
29    }
30   }
31  }
32  printf("%d\n",f1[1][n]);
33  return 0;
34 }
35 /**/
View Code

 

posted @ 2020-03-13 21:05  hyghb  阅读(197)  评论(0编辑  收藏  举报