A
将 (n) 质因数分解为 (prodlimits_{i=1}^m p_i^{c_i}),那么奇数因子的个数为 (prodlimits_{1le ile moperatorname{and}p_i ot=2} c_i+1),偶数因子个数为 (c'cdotprodlimits_{1le ile moperatorname{and}p_i ot=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) 的贡献,写成式子就是:
枚举到 (i-1) 的时候实际上已经计算了 (sumlimits_{j=1}^{i-2}2^{i-j-2}a_j),所以到 (i) 时无需重新计算,直接 ( imes 2+a_{i-1}) 即可。
时间复杂度为 (mathcal O(nlog 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{lfloorlog_2m floor,n}le 18),所以不同的数不会超过 (18) 个,记为 (p)。假设我们已经知道了选哪 (k) 个数,那么给答案的贡献就是 (mathrm C_{n-1}^{k-1}),也就是 (n) 个位置分给 (k) 个数的方案数(隔板法)。如果知道选择 (k) 个数的方案数 (sigma_i),那么答案就是 (sumlimits_{i=1}^{p} sigma_imathrm 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);(kge2,ikle m)),这样就可以得到 (sigma_i=sumlimits_{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) 组((0le ile log_2 m))的物品体积为 (2cdot2^i,4cdot2^i,6cdot 2^i,cdots,2kcdot 2^i)((k) 是正整数,(2kcdot 2^ile m)),问有多少种方案能够凑出 (m)。假设我们在这一组取的物品为 (2k'cdot 2^i),那么分给 (n) 个数就有 (mathrm C_n^{2k'})((n) 个位置选 (2k') 个)选法,即如果令 (f(j)) 表示凑出 (j) 的方案数,则有:
复杂度在 (mathcal O(nlog 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;
}