CSP 提高组模拟6 题解

T1 花间叔祖

其实是化简数组。赛时94pts,因为少加了abs挂6pts。

题目描述

给你一个序列 \(A\),选择一个数 \(p\ge2\),使得整个序列 \(\mod p\) 后数字的种类最少。序列长度满足 \(2\le N\le2\times10^5\),序列中的数值满足 \(0\le A_i\le10^9\)

部分分

这题拿部分分的就是纯挂分。

正解

思路

首先来个小结论:若 \(a\equiv b\pmod p\),则 \(p\) 的取值为 \({a-b}\over{k}\), \(k\mid(a-b)\)

证明如下:

因为 \(a\equiv b\pmod p\),所以\(a\mod p=b\mod p\),即存在两个自然数 \(x\),\(y\) 满足\(a-x\times p=b-y\times p\)

移项,得 \(a-b=(x-y)\times p\)

\(x-y\) 除到等式左边,可得 \({a-b}\over{x-y}\)\(=p\)

由于 \(p\) 是正整数,所以 \(x-y\mid a-b\),证毕。

然后来看题目,发现输出的结果只能是 \(1\)\(2\),因为一个序列 \(\mod2\) 最多只可能会出现两种数字,即 \(0\)\(1\),如果答案是 \(1\),那么这个序列一定满足存在一个 \(p\ge2\),使得 \(A\) 中的每一项 \(\mod p\) 结果相同。根据我们在最开始推导的柿子,\(p\) 可以被表示为 \(A_i\) 递差的 \(gcd\)。求出来判断是不是 \(1\) 即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ri register int
#define inf 0x3f3f3f3f
int a,b[200002],re;
int main()
{
	scanf("%d",&a);
	for(ri i=1;i<=a;i++)
	{
		scanf("%d",&b[i]);
	}
	for(ri i=2;i<=a;i++)
	{
		re=__gcd(abs(b[i]-b[i-1]),re);//细节:要么sort一遍,要么+abs
		if(re==1)
		{
			puts("2");
			exit(0);
		}
	}
	puts("1");
	return 0;
}
/*

*/

T2 合并r

赛时60pts,但这题暴力的极限是75pts。数组开小了挂15pts

题目描述

给你两个整数 \(n\)\(k\),规定满足条件的实数为 \(1\over2^i\)\(i\) 为自然数,求出用 \(n\) 个满足条件的实数拼出 \(k\) 的所有不同方案数,答案对 \(998244353\) 取模。

部分分

10pts

特判 \(n=1\),输出 \(1\)

?pts

暴搜,反正我没打。

75pts

考虑的\(k=2\)时的值,对于\(1\)来说是两个,对\(1\over2\)来说就是四个,对\(1\over4\)来说是八个……以此类推,设计记搜,\(dp[i][j]\)表示\(i\)个数相对于它本身空间为\(j\)时的情况,不难得出状态转移方程:$$dp[i][j]=\sum_{k=0}^{i}dp[i-k][j\times2]$$
加点边界判定,数组开大,75pts到手。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ri register int
#define inf 0x3f3f3f3f
const int mod=998244353;
int a,b;
long long dp[5005][5005];
long long dfs_pro(int x,int y)
{
	if(y>x||y==0)
	{
		return 0;
	}
	if(x==y)
	{
		return 1;
	}
	if(dp[x][y]>0)
	{
		return dp[x][y];
	}
	long long rn=0;
	for(ri i=0;i<=y;i++)
	{
		rn+=dfs_pro(x-i,(y-i)<<1);
		rn%=mod;
	}
	dp[x][y]=rn;
	return dp[x][y];
}
int main()
{
	scanf("%d%d",&a,&b);
	printf("%lld",dfs_pro(a,b));
	return 0;
}
/*

*/

正解

思路

考虑如果当前凑出了 \(k\),那么把这之中的所有之都 \(\div2\),就可以得到 \(k\over2\)。这样的话,我们找到的所有情况中的数值都是一点一点 \(\div2\)操作出来的,初始的值就应该是 \(1\),所有添加值不是 \(1\) 的情况都可以被 \(\div2\) 的情况覆盖,只需考虑 \(+1\)\(\div2\) 即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ri register int
#define inf 0x3f3f3f3f
const int mod=998244353;
int a,b;
long long dp[5005][5005];
int main()
{
	scanf("%d%d",&a,&b);
	dp[0][0]=1;//初始化
	for(ri i=1;i<=a;i++)
	{
		for(ri j=i;j>=1;j--)//由于需要调用j*2的值,所以要倒序
		{
			dp[i][j]=dp[i-1][j-1];
			if((j<<1)<=i)
			{
				dp[i][j]+=dp[i][j<<1];
			}
			dp[i][j]%=mod;
		}
	}
	printf("%lld",dp[a][b]);
	return 0;
}
/*

*/
posted @ 2024-07-24 08:15  一位很会的教授er~  阅读(67)  评论(0)    收藏  举报