大致题意:告诉你两个人计算美丽数字的方法。对于ZSTU来说,先把几何看作序列,每个序列的美丽数字是这个序列所有子区间的gcd之和,每个集合的美丽数字是这个集合所有排列序列的美丽数字之和。对于HDU来说,在集合中挑选k个数字,这k个数字的美丽数字是他们的gcd*k,这个集合的美丽数字是k的所有取值和所有挑选方法的美丽数字之和。现在让你分别在mod的意义下计算两个人的美丽数字之和,问最后在mod的意义下哪一个更大。
对于ZSTU来说,这个问题还是比较棘手的。首先,我肯定是要计算每一个gcd的贡献,不可能去枚举 n! 种集合的表示方法。当gcd为x的时候,我们考虑定义 表示从所有元素种取k个且这k个数字的gcd是x的方案数。然后再去考虑排列顺序,把这k个元素内部捆绑,有k!种方式,然后外面与其他数字形成n-k+1个部分,一个全排列(n-k+1)!,那么最后的答案就是,其中的cnt[x]表示集合种x的倍数的个数:
现在我们来考虑这个。我们不妨设 表示从所有元素种选择k个且它们的gcd是x的倍数的方案数。显然,g和f是有关系的,有 ,然后我们可以推出g的公式: 。那么,我们就可以进行莫比乌斯反演了。根据反演公式有:
答案可以写成:
交换求和次序:
注意到最后一项 只与d相关,所以可以考虑预处理,对于每一个d只需要枚举所有因子,计算求和即可。可以在时间复杂度内完成预处理,我们不妨令。那么答案可以写成:
把组合数展开:
还是一样注意到右边的式子,我们可以单独考虑。我们不妨设b(d)表示右边那个和式。这里我就不一一赘述,转载一个大神的博客内容,他的P(x)就对应我的b(d):
按照他所说的方法进行化简,可以得到: ,那么对于ZSTU来说,最后的结果就是:
然后我们来考虑HDU怎么算。HDU是计算所有的取K个数字的方案对应的gcd*k之和。同样的,我们还是得按照gcd计算贡献。这里一些函数的定义与计算ZSTU的时候一样,不再赘述。可以得到,HDU的结果为:
相对来说比较容易理解和计算,还是一样的,我们对f进行莫比乌斯反演,借助辅助函数g,这里就省略过程直接代入:
和ZSTU一样的交换求和次序,然后用h(d)替换右边:
对于右边的式子,我们把和式展开,然后首尾相加(组合数首尾相同),我们发现它等于,那么HDU的答案就可以写成:
到这里,我们总算是大功告成啦……比较一下两个的大小即可。
总结一下,就是需要预处理阶乘、逆元、阶乘逆元、二的幂、h(d)和b(d)以及cnt[d]。把这些求完之后,我们就可以O(N)的求HDU和ZSTU的值。然后预处理的过程求h(d)是,求cnt[d]是,其余的用线性方法都是的,那么总的复杂度就是这里面忽略了线性的初始化和每一次回答问题。具体见代码:
#include<bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define mod 258280327
#define LL long long
#define N 100010
using namespace std;
int ifac[N],fac[N],inv[N],mu[N],p[N],pw[N];
int n,m,h[N],num[N],cnt[N],b[N];
bool isp[N];
void init()
{
int sz=0;
isp[1]=mu[1]=pw[0]=1;
for(int i=2;i<N;i++)
{
if(!isp[i])p[++sz]=i,mu[i]=-1;
for(int j=1;j<=sz&&i*p[j]<N;j++)
{
isp[i*p[j]]=1;
if(i%p[j]==0){mu[p[j]*i]=0;break;}
else mu[p[j]*i]=-mu[i];
}
}
pw[1]=2;
fac[0]=inv[0]=ifac[0]=1;
fac[1]=inv[1]=ifac[1]=1;
for(int i=2;i<N;i++)
{
pw[i]=pw[i-1]*2LL%mod;
fac[i]=fac[i-1]*(LL)i%mod;
inv[i]=(mod-mod/i)*(LL)inv[mod%i]%mod;
ifac[i]=ifac[i-1]*(LL)inv[i]%mod;
}
}
void get_h()
{
for(int i=1;i<N;i++)
for(int j=1;j*j<=i;j++)
{
if (i%j) continue;
h[i]+=j*mu[i/j];
if (j*j!=i) h[i]+=i/j*mu[j];
}
}
int main()
{
// file(g);
IO; init(); get_h();
while(cin>>n)
{
for(int i=1;i<=m;i++)
num[i]=cnt[i]=0;m=0;
for(int i=1;i<=n;i++)
{
int x; cin>>x;
m=max(m,x);num[x]++;
}
for(int i=1;i<=m;i++)
for(int j=i;j<=m;j+=i)
cnt[i]+=num[j];
for(int i=1;i<=n;i++)
b[i]=fac[n+1]*(LL)ifac[i-1]%mod*inv[n-i+2]%mod;
int ZSTU=0,HDU=0;
for(int i=1;i<=m;i++)
{
if (!cnt[i]) continue;
HDU+=cnt[i]*(LL)pw[cnt[i]-1]%mod*h[i]%mod;
ZSTU+=h[i]*(LL)fac[cnt[i]]%mod*b[cnt[i]]%mod;
if (ZSTU>=mod) ZSTU-=mod;
if (HDU>=mod) HDU-=mod;
}
if (ZSTU==HDU) cout<<"Equal "<<HDU<<endl;
if (HDU>ZSTU) cout<<"Mr. Hdu "<<HDU<<endl;
if (ZSTU>HDU) cout<<"Mr. Zstu "<<ZSTU<<endl;
}
return 0;
}