今天是2021.9.23,下午刷了两道1400,晚上有场div2,打完又刷了一道。
1348.B. Phoenix and Beauty(构造)
大意:
构造一个序列,使序列中长度为k的子序列的元素之和始终为m。
思考:
让序列中长度为k的子序列元素之和都为m,那这个子序列一定是abcd abcd abcd…(abcd长度为k)
如果元素种类超过m,就构造不出。
因为所给序列长度不超过100,构造序列长度不超过10000,所以可以输出100组子序列。
构造长度为m,包含所有种类的子序列就行了。
Code:
const int N = 200010;
int T, n, m, a[N];
bool f[110];
int cnt;
int main(){
Ios;
cin>>T;
while(T--)
{
cin>>n>>m;
mem(f,0);
cnt=0;
for(int i=1;i<=n;i++)
{
int x;cin>>x;
if(!f[x]) f[x]=1,a[++cnt]=x;
}
if(cnt>m){
cout<<-1<<"\n";
continue;
}
for(int i=cnt+1;i<=m;i++) a[i]=a[i-1];
cout<<100*m<<"\n";
for(int i=1;i<=100;i++){
for(int j=1;j<=m;j++){
cout<<a[j]<<" ";
}
}
cout<<"\n";
}
return 0;
}
689.A. Vacations (dp)
大意:
每天最多有两种选择,但是不能连续两天都是同一选择。
问,最少休息多少天?
思考:
思路1:dfs
直接暴搜,超强剪枝:1.cnt超过ans退出;2.递归次数限制
思路2:dp
二维dp,将三种状态新开一维。
对于每个点,寻找当前状态的来源。
Code:
const int N = 200010;
int T, n, m, a[N];
int ans=1e9,cnt;
int step,flag;
int f[N][5];
void dfs(int u,int last)
{
step++;
if(step>=10000000){
flag=1;return;
}
if(flag||cnt>=ans) return;
if(u==n+1){
ans=min(ans,cnt);
return;
}
if(a[u]==0){
cnt++;
dfs(u+1,0);
cnt--;
}
else if(a[u]==1)
{
if(last==1) cnt++,dfs(u+1,0),cnt--;
else dfs(u+1,1);
}
else if(a[u]==2){
if(last==2) cnt++,dfs(u+1,0),cnt--;
else dfs(u+1,2);
}
else{
if(last!=1) dfs(u+1,1);
if(last!=2) dfs(u+1,2);
}
}
void DP()
{
for(int i=1;i<=n;i++) f[i][0]=f[i][1]=f[i][2]=-1e9;
for(int i=1;i<=n;i++)
{
f[i][0]=max(f[i-1][0],max(f[i-1][1],f[i-1][2]));
if(a[i]==1||a[i]==3) f[i][1]=max(f[i-1][0],f[i-1][2])+1;
if(a[i]==2||a[i]==3) f[i][2]=max(f[i-1][0],f[i-1][1])+1;
}
ans=n-max(f[n][0],max(f[n][1],f[n][2]));
}
int main(){
Ios;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
// dfs(1,0);
DP();
cout<<ans;
return 0;
}
515.C. Drazil and Factorial (思维)
大意:
定于运算F(x),表示其所有位数阶乘的乘积。(x可能有前导0)
现给定x,要找到最大的a,使得F(a)=F(x),且a的每一位都不能为0或1。
思考:
要让a最大,就让x分解出的因子最多,就是分解出质因子,然后组成的a尽量长。
因为重组的a长度一定,要让a最大,就先将大的质因子输出。
因为F(a)=F(x),所以输出一个值x的时候,要把2~x的所有值的个数-1。
Code:
const int N = 200010;
int T, n, m, a[N];
int f[10][10]={{0},{1},{2},{3},{2,2},{5},{3,2},{7},{2,2,2},{3,3}};
int t[10]={0,1,1,1,4,1,2,1,3,2};
int cnt[10];
int main(){
cin>>n;
for(int i=1;i<=n;i++)
{
char c;cin>>c;
int x=c-'0';
for(int j=2;j<=x;j++)
{
for(int k=0;k<t[j];k++)
{
cnt[f[j][k]]++;
}
}
}
for(int i=9;i>=2;i--)
{
while(cnt[i])
{
for(int j=i;j>=2;j--)
{
for(int k=0;k<t[j];k++){
cnt[f[j][k]]--;
}
}
cout<<i;
}
}
return 0;
}
Codeforces Round #745 (Div. 2)
A. CQXYM Count Permutations (数论,思维)
大意:
给数n,问2n个数的全排列中,一共有多少序列满足: a i < a i + 1 ai<ai+1 ai<ai+1 的个数不少于 n。 n ≤ 1 e 5 n ≤1e5 n≤1e5,答案对 1 e 9 + 7 1e9+7 1e9+7取模。
思考:
这道题第一眼看上去是毫无头绪的,心想,艹,这是A题?
最后过掉才明白出题人的用意。
关键点在于:满足条件的位置个数不少于n,这个n很特殊,是突破点。
加上是2n个数的全排列,可以发现,2n个数的全排列中,一共有一半是满足条件的。
所以这道题就是要求:2n的全排列个数 / 2。
但是有个技巧的地方:
n很大,到1e5,如果边乘边取模,最后再除2,就和原来答案不一样了。
但是由于答案为 123*…*2n,最终再除2,所以就提前约分,将2约掉,从3开始往后乘,边乘边取模。这样就是正确的了。
Code:
const int N = 200010, mod=1e9+7;
int T, n, m, a[N];
int main(){
Ios;
cin>>T;
while(T--)
{
cin>>n;
if(n==1){
cout<<1;
cout<<"\n";
continue;
}
else if(n==2){
cout<<12;
cout<<"\n";
continue;
}
ll ans=1;
for(int i=3;i<=2*n;i++)
{
ans=ans*i%mod;
}
cout<<ans<<endl;
}
return 0;
}
B. Diameter of Graph (思维)
大意:
给n个点,m条边,问能否构成一个无向连通图,其两点间的最小距离中的最大值小于k-1。(没有重边和自环)
思考:
刚看到同样没有思路。
先分析m的范围限制:
因为是连通图,所以m要不小于n-1;
因为没有自环和重边,所以边数m最大不超过
C
n
2
=
n
∗
(
n
−
1
)
/
2
C_n^2 = n*(n-1)/2
Cn2=n∗(n−1)/2。
所以 n-1 ≤ m ≤ n*(n-1)/2。
发现,当 m ∈ [n-1, n*(n-1)/2 ) 时,最小距离中的最大值都是2;
当 m = n*n(n-1)/2,也就是完全图,最小距离都为1。
所以,先判断m是否再范围中内,在判断k的大小就行了。
注意,特判点数n为1的情况。
Code:
const int N = 200010;
int T, n, m, a[N];
int main(){
cin>>T;
while(T--)
{
int k;
cin>>n>>m>>k;
if(n==1){
if(k>=2&&m==0) cout<<"yes\n";
else cout<<"no\n";
}
else if(m<n-1||m>(ll)n*(n-1)/2) cout<<"no\n";
else if(k>=4) cout<<"yes\n";
else if(k==3){
if(m==(ll)n*(n-1)/2) cout<<"yes\n";
else cout<<"no\n";
}
else cout<<"no\n";
}
return 0;
}
这场确实有点难了。
要不是队友搁旁边,准得掉大分。。