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 /**/
反思:记住这个小小的结论吧。
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 /**/
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 /**/
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 /**/
反思:我在推出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 /**/