提莫
A - Two Regular Polygons
问题:判断能否通过对正n边形选取m个顶点,形成中心重合且顶点为选取的m个点的正m变形
答案:判断n是否整除m即可,是则YES,否则NO。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ll t;
cin>>t;
while(t--){
ll n,m;
cin>>n>>m;
if(n%m)puts("NO");
else puts("YES");
}
}
B - Bogosort
问题:给一个数组a,对其重新排序使得对于任意
i
<
j
i<j
i<j满足
j
−
a
j
≠
i
−
a
i
j-a_j\not=i-a_i
j−aj=i−ai
答案:按从大到小排序即可,使得
i
<
j
i<j
i<j时
a
i
>
a
j
a_i>a_j
ai>aj
就一定可以满足
j
−
a
j
>
i
−
a
i
j-a_j>i-a_i
j−aj>i−ai
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[10001];
int main(){
ll t;
cin>>t;
while(t--){
ll n;
cin>>n;
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
sort(a+1,a+1+n);
for(ll i=n;i>=1;i--){
printf("%lld%c",a[i],i==1?'\n':' ');
}
}
}
C - Adding Powers
问题:一个数组v,初始为0,长度为n,给一个底数k
再给一个长为n的目标数组a,问是否可通过如下操作经过若干步将数组v变成数组a。
操作:
每一步有两个选择,设当前步数为i(i从0开始编号)有:
1、选择一个位置pos,将
v
p
o
s
v_{pos}
vpos加上
k
i
k^i
ki
2、跳过这一步
答案:
从数组a倒推看是否可以减到0数组而不发生矛盾,对每个位置pos的数每次减去可以减去的最大的
k
i
k^i
ki且每个i只能使用一次,即每次都选择使
a
p
o
s
−
k
i
≥
0
a_{pos}-k^i\geq0
apos−ki≥0成立的最大的i使
a
p
o
s
a_{pos}
apos减去
k
i
k^i
ki,若存在使用的i重复或者数组中某个数不能减到0,则输出NO,否则输出YES。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<int,bool>mp;
int main(){
int t;
cin>>t;
while(t--){
mp.clear();
int n,k;
cin>>n>>k;
bool flag=true;
for(int i=1;i<=n;i++){
ll x;
scanf("%lld",&x);
if(!x)continue;
ll cur=0;
ll pw=1;
while(x>=pw*k){
pw*=k;
cur++;
}
while(cur>=0){
if(x>=pw){
if(mp.count(cur)){
flag=false;
}
else{
mp[cur]=true;
}
x-=pw;
}
pw/=k;
cur--;
}
if(x)flag=false;
}
if(flag){
puts("YES");
}
else{
puts("NO");
}
}
}
D - Count the Arrays
问题:给两个整数n和m,计算满足以下条件的数列的个数:
1、数列有n个数
2、每个数都属于区间
[
1
,
m
]
[1,m]
[1,m]
3、数列中有一对相同的数
4、数列存在位置j使[1,j]严格单调递增,[j,m]严格单调递减
答案:组合数学问题,分如下步骤:
1、因数列中有一对重复,从[1,m]中选取n-1个数,方法有
C
m
n
−
1
C_m^{n-1}
Cmn−1种;
2、n-1个数中有一个重复使用,但不能是n-1中最大的数,因为最大的数若有两个,一定不满足严格单增或单减的条件,有
(
n
−
2
)
(n-2)
(n−2)种取法,将重复的数分别放在最大的数两边;
3、对于剩下的
(
n
−
3
)
(n-3)
(n−3)可按顺序任意选择放在最大的数的左侧还是右侧,有
2
n
−
3
2^{n-3}
2n−3种方法。
综上,
n
≤
2
n\le2
n≤2时方法数为0
否则方法数为
C
m
n
−
1
∗
(
n
−
2
)
∗
2
n
−
3
C_m^{n-1}*(n-2)*2^{n-3}
Cmn−1∗(n−2)∗2n−3。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=998244353 ;
ll inv(ll a) {
return a == 1 ? 1 : (ll)(MOD - MOD / a) * inv(MOD % a) % MOD;
}
ll modexp(ll a, ll b)
{
ll res=1;
while(b>0)
{
if(b&1)
res=res*a%MOD;
b=b>>1;
a=a*a%MOD;
}
return res;
}
ll C(ll n,ll m)
{
if(m < 0)return 0;
if(n < m)return 0;
if(m > n-m) m = n-m;
ll up = 1, down = 1;
for(ll i = 0 ; i < m ; i ++){
up = up * (n-i) % MOD;
down = down * (i+1) % MOD;
}
return up * inv(down) % MOD;
}
int main(){
ll n, m;
cin>>n>>m;
if(n==2)cout<<0<<endl;
else{
ll ans=C(m,n-1)*(n-2);
ans%=MOD;
ans*=modexp(2,n-3);
ans%=MOD;
cout<<ans<<endl;
}
}
E - Array Shrinking
问题:给一个长为n的数列a,可以将两个相邻且相等的数字合并并为原来的数字加1,可重复若干次,问可得到的最短的序列长度时多少。
答案:用
c
[
l
]
[
r
]
c[l][r]
c[l][r]表示
[
l
,
r
]
[l,r]
[l,r]能否合成一个点,
d
p
[
i
]
dp[i]
dp[i]表示只考虑前
i
i
i位可以得到的最短序列长度。
动态规划转移方程是
i
f
(
c
[
i
]
[
j
]
=
=
t
r
u
e
)
if(c[i][j]==true)
if(c[i][j]==true)
d
p
[
j
]
=
m
i
n
(
d
p
[
j
]
,
d
p
[
i
−
1
]
+
1
)
;
dp[j]=min(dp[j],dp[i-1]+1);
dp[j]=min(dp[j],dp[i−1]+1);
判断
c
[
l
]
[
r
]
c[l][r]
c[l][r]的过程可用栈来优化,去掉一个n,
均摊复杂度
O
(
n
2
)
O(n^2)
O(n2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=0x3f3f3f3f;
ll dp[501];
bool c[501][501];
ll a[501];
int main(){
ll n;
cin>>n;
memset(dp,INF,sizeof(dp));
for(ll i=1;i<=n;i++){
cin>>a[i];
}
for(ll i=1;i<=n;i++){
stack<ll>s;
for(ll j=i;j<=n;j++){
ll cur=a[j];
while(!s.empty()&&s.top()==cur){
s.pop();
cur++;
}
s.push(cur);
if(s.size()==1){
c[i][j]=true;
}
}
}
dp[0]=0;
for(ll i=1;i<=n;i++){
for(int j=i;j<=n;j++){
if(c[i][j]==true)
dp[j]=min(dp[j],dp[i-1]+1);
}
}
cout<<dp[n]<<endl;
}