Codeforces Round #563 (Div. 2) (B找规律、C数论(质数)、D异或前缀和、E线性dp)

心得

自己的cf不知道为什么交不了题了,用的归神的号还给归神掉分了,wtcl……

而且明显觉得自己前中期题代码比题解复杂好多,AC之后,还是补一补简单的做法比较好

CF的这种代码不长考验思维与1y的题,最培养综合能力了

思路来源

https://codeforces.com/blog/entry/67388

B.Ehab Is an Odd Person

Q:1e5个数,每个数在1到1e9之间,可以交换和为奇数的两个位置的数,输出最后字典序最小的序列

A:如果只有奇数或只有偶数,就直接输出原序列,

否则借助一个奇数一定可以令两个偶数换位,且奇数位置不变

数学归纳一下,就是可以令偶数内部排序,奇数内部排序,且奇数和偶数之间还可以任意换,那就是sort一下的结果

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,a[maxn],b[2];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		b[a[i]&1]++;
	}
	if(b[0]&&b[1])sort(a+1,a+1+n);
	for(int i=1;i<=n;++i)
	printf("%d%c",a[i],i==n?'\n':' ');
	return 0;
}

C.Ehab and a Special Coloring Problem

Q:给下标为2到n的位置每个分配一个1到n之间的数,使得下标互质的(i,j)分配的数不同,且令最大的值最小,输出分配序列

A:注意到质数每个都需要新开一个数,且合数只需和其任意一个质因子的取值相同即可,

这样对于互质的两个合数来说,它们分配的数只与其质因子取值相同,而这两个合数没有公共的质因子,所以取值不同

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,a[maxn],cnt;
bool p[maxn];
int main()
{
	scanf("%d",&n);
	for(int i=2;i<=n;++i)
	{
		if(!p[i])a[i]=++cnt;
		for(int j=2*i;j<=n;j+=i)
		a[j]=a[i],p[j]=1;
	}
	for(int i=2;i<=n;++i)
	printf("%d%c",a[i],i==n?'\n':' ');
	return 0;
}

D.Ehab and the Expected XOR Problem

Q:n(n<=18),给定一个x(1<=x<(1<<18)),要求输出一个长为len的序列,

序列的每个值只能在1到1<<n之间,在最大化len的前提下,

使得序列的任意一个子区间的异或值都不等于0或给定的x

A:考虑前缀异或和,则问题转化成,

任意一个子区间[L,R]的异或和=Xor[R]^Xor[L-1],

在前缀和序列中,即不能有两个下标i,j满足Xor(i)^Xor(j)==x,

①如果x在1到(1<<n)之间,在1到(1<<n)的序列中,异或值为x的(i,j)成对出现,互不影响

所以当异或前缀和用到了一个,就不能用另一个,把另一个标记用过即可

②如果x大于1<<n,任两个前缀值异或起来都不为x,随便取

输出时输出相邻两项异或值,类似前缀和作差还原回去即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
bool used[1<<18];
int n,x,m;
int a[1<<18],cnt;
int main()
{
	scanf("%d%d",&n,&x);
	m=1<<n;m--;
	used[x]=1;used[0]=1; 
	for(int i=m;i>=1;--i)
	if(!used[i])
	{
		a[++cnt]=i;
		used[i^x]=1; 
	}
	printf("%d\n",cnt);
	for(int i=1;i<=cnt;++i)
	printf("%d%c",a[i]^a[i-1],i==cnt?'\n':' ');
	return 0;
} 

E.Ehab and the Expected GCD Problem

Q:对于一个给定的1-n的排列,定义g(i)为其前i个数的gcd值,

定义f(p)为1到n这n个g(i)的值中数字不同的数的个数,fmax(p)为所有排列p中f(p)最大的值

给定n(2<=n<=1e6),求有多少排列p满足,f(p)==fmax(p),答案%(1e9+7)

A:

考虑到连续子段gcd会降,一次最少降一个2,降log次就降没了

所以,能降最多次数的话,就是一次降一个2,那么开头的数应该是不大于n的2的最大幂次

这个数把其中一个2替换成3也是可以的,如果新出的这个数不超过n的话,

替换成别的质数就不如替换成两个2或更多的2更优了,

 

定义dp[i][x][y]为当前放到第i个数,当前gcd为2^{x}*3^{y}的方案数

定义f(x,y)为不超过n的2^{x}*3^{y}的倍数的个数,显然f(x,y)=[\frac{n}{2^{x}*3^{y}}]

①可以往后加一个2^{x}*3^{y}的倍数,后面还没用过的2^{x}*3^{y}的倍数个数是f(x,y)-i

②可以往后加一个2^{x-1}*3^{y}的倍数,且不能是2^{x}*3^{y}的倍数,不然和①重复了

③可以往后加一个2^{x}*3^{y-1}的倍数,且不能是2^{x}*3^{y}的倍数,不然和①重复了

 

考虑初始情况,第一个值可以填不超过n的最大的2的幂次,dp[1][x][0]=1

如果可以在不超过n的前提下,把其中一个2换成3的话,dp[1][x-1][1]=1,

由于序列中一定会有1,所以最后序列尾的gcd一定为1,答案即为dp[n][0][0]

 

想了很久,f(x,y)-i变负数了咋办,后来觉得是在f(x,y)-i恰为0时,后面的dp[i+1][x][y]就恒为0,不会再被更新了

只会降序更新,f(x,y)-i为0的时候,说明x'>=x或y'>=y的f(x',y')-i<=0,dp[i][x'][y']早已是0了,不会用0去更新dp[i+1][x][y]的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e6+10;
int n,p;
int dp[maxn][21][2];
int f(int x,int y)
{
	int tmp=(1<<x);
	if(y)tmp*=3;
	return n/tmp;
}
int main()
{
	scanf("%d",&n);
	while((1<<p)<=n)p++;
	p--;
	dp[1][p][0]=1;
	if((1<<(p-1))*3<=n)
	dp[1][p-1][1]=1;
	for(int i=1;i<n;++i)
	{
		for(int x=0;x<=p;++x)
		{
			for(int y=0;y<=1;++y)
			{
				dp[i+1][x][y]=(dp[i+1][x][y]+1LL*dp[i][x][y]*(f(x,y)-i))%mod;
				if(x)dp[i+1][x-1][y]=(dp[i+1][x-1][y]+1LL*dp[i][x][y]*(f(x-1,y)-f(x,y)))%mod;
				if(y)dp[i+1][x][y-1]=(dp[i+1][x][y-1]+1LL*dp[i][x][y]*(f(x,y-1)-f(x,y)))%mod;
			}
		} 
	}
	printf("%d\n",dp[n][0][0]);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值