AtCoder Regular Contest 116

A

\(n\) 质因数分解为 \(\prod\limits_{i=1}^m p_i^{c_i}\),那么奇数因子的个数为 \(\prod\limits_{1\le i\le m\operatorname{and}p_i\not=2} c_i+1\),偶数因子个数为 \(c'\cdot\prod\limits_{1\le i\le m\operatorname{and}p_i\not=2} c_i+1\)\(c'\) 为质因子 \(2\) 的指数,算一下 \(c'\) 的大小即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		ll n;
		scanf("%lld",&n);
		if(n%2==0&&n%4!=0) puts("Same");
		else if(n&1ll) puts("Odd");
		else puts("Even");
	}
	return 0;
}

B

套路题。

先 sort 一遍。从 \(a_1\)\(a_n\) 枚举 \(\max(B)\),此时能够当作 \(\min(B)\) 的只有这个数及它之前的数。假设当前枚举到的数是 \(a_i\),钦定一个数 \(a_j\)\(j<i\))为 \(\min(B)\)(对于 \(\min(B)=\max(B)\) 的情况单独计算即可),那么以 \(a_i\)\(\max\),以 \(a_j\)\(\min\) 的子序列共 \(2^{i-j-1}\) 个,也就是说能够给答案带来 \(2^{i-j-1}a_ia_j\) 的贡献,写成式子就是:

\[\sum\limits_{i=1}^n\left(a_i\sum\limits_{j=1}^{i-1}2^{i-j-1}a_j\right)+\sum\limits_{i=1}^n a_i^2 \]

枚举到 \(i-1\) 的时候实际上已经计算了 \(\sum\limits_{j=1}^{i-2}2^{i-j-2}a_j\),所以到 \(i\) 时无需重新计算,直接 \(\times 2+a_{i-1}\) 即可。

时间复杂度为 \(\mathcal O(n\log n)\)(排序的复杂度)。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
inline void read(int &x)
{
	x=0;int f=1;
	char c=getchar();
	while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	x*=f; 
}
const int N=2e5+10;
int a[N];
signed main()
{
	int n;read(n);
	for(int i=1;i<=n;i++)read(a[i]);
	sort(a+1,a+n+1);
	int sum=0,ans=0;
	for(int i=1;i<=n;i++)
	{
		sum=(sum*2+a[i-1])%998244353;
		ans+=a[i]*(a[i]+sum);ans%=998244353;
	}
	printf("%lld",ans);
	return 0;
}

C

非常有意思的一道题,感谢 这篇题解

因为 \(\min\{\lfloor\log_2m\rfloor,n\}\le 18\),所以不同的数不会超过 \(18\) 个,记为 \(p\)。假设我们已经知道了选哪 \(k\) 个数,那么给答案的贡献就是 \(\mathrm C_{n-1}^{k-1}\),也就是 \(n\) 个位置分给 \(k\) 个数的方案数(隔板法)。如果知道选择 \(k\) 个数的方案数 \(\sigma_i\),那么答案就是 \(\sum\limits_{i=1}^{p} \sigma_i\mathrm C_{n-1}^{i-1}\),问题就转化成了:选 \(k\) 个值域在 \([1,m]\) 内的不同的数,求方案数。令 \(f(i,j)\) 表示当第 \(j\) 个数的值为 \(i\) 的方案数,那么可以进行主动更新 \(f(ik,j+1)\gets f(ik,j+1)+f(i,j)\;(k\ge2,ik\le m)\),这样就可以得到 \(\sigma_i=\sum\limits_{j=1}^m f(j,i)\),问题解决。

总结:问题的突破口在于发现「所以不同的数不会超过 \(18\) 个」,此类问题的关键点是需要找到这些较小的值或者特殊性质,无思路是多观察这些东西。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=2e5+10;
int p[N],inv[N];
int C(int n,int m) {return (long long)p[n]*inv[m]%998244353*inv[n-m]%998244353;}
int qpow(int a,int n)
{
	a%=998244353;
	int ans=1;
	while(n)
	{
		if(n&1) ans=(long long)ans*a%998244353;
		a=(long long)a*a%998244353;
		n>>=1;
	}
	return ans;
}
int sum[N],f[N][30];
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	p[0]=inv[0]=1;
	for(int i=1;i<=n;i++) 
	{
		p[i]=(long long)p[i-1]*i%998244353;
		inv[i]=qpow(p[i],998244351);
	}
	int p=min((int)(log(m)/log(2))+1,n);
	for(int i=1;i<=m;i++) f[i][1]=1;
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=p;j++)
		{
			for(int k=2;k*i<=m;k++)
			{
				f[k*i][j+1]+=f[i][j];
				f[k*i][j+1]%=998244353;
			}
		}
	}
	for(int i=1;i<=p;i++)
	{ 
		for(int j=1;j<=m;j++)
		{
			sum[i]+=f[j][i];
			sum[i]%=998244353;
		}
	}
	int ans=0;
	for(int i=1;i<=p;i++)
	{
		ans+=(long long)C(n-1,i-1)*sum[i]%998244353;
		ans%=998244353;
	}
	printf("%d",ans);
	return 0; 
}

D

由于异或和为 \(0\),所以每一位中 \(1\) 的出现次数都是偶数。可以转化成模型:现在有 \(\log_2 m\) 个组,第 \(i\) 组(\(0\le i\le \log_2 m\))的物品体积为 \(2\cdot2^i,4\cdot2^i,6\cdot 2^i,\cdots,2k\cdot 2^i\)\(k\) 是正整数,\(2k\cdot 2^i\le m\)),问有多少种方案能够凑出 \(m\)。假设我们在这一组取的物品为 \(2k'\cdot 2^i\),那么分给 \(n\) 个数就有 \(\mathrm C_n^{2k'}\)\(n\) 个位置选 \(2k'\) 个)选法,即如果令 \(f(j)\) 表示凑出 \(j\) 的方案数,则有:

\[f(j)\gets f(j)+\mathrm C_{n}^{2k}f(j-2k\cdot 2^i) \]

复杂度在 \(\mathcal O(n\log n)\) 级别。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=5010;
int qpow(int a,int n)
{
	int ans=1;
	while(n)
	{
		if(n&1) ans=(long long)ans*a%998244353;
		a=(long long)a*a%998244353;
		n>>=1;
	}
	return ans;
}
int f[N],p[N],inv[N];
int C(int n,int m) {return (long long)p[n]*inv[m]%998244353*inv[n-m]%998244353;}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	p[0]=inv[0]=1;
	for(int i=1;i<=n;i++)
	{
		p[i]=(long long)p[i-1]*i%998244353;
		inv[i]=qpow(p[i],998244351);
	}
	f[0]=1;
	for(int i=0;(1<<i)<=m;i++)
		for(int j=m;j>=0;j--)
			for(int k=1;2*k*(1<<i)<=j;k++)
				if(j-2*k*(1<<i)>=0)
				{ 
					f[j]+=(long long)f[j-2*k*(1<<i)]*C(n,2*k)%998244353;
					f[j]%=998244353;
				}
	printf("%d",f[m]);
	return 0;
}
posted @ 2021-03-31 23:02  zzt1208  阅读(97)  评论(0编辑  收藏  举报