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\) 的贡献,写成式子就是:
枚举到 \(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\) 的方案数,则有:
复杂度在 \(\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;
}