前言
上午模拟赛考了这个题
虽然之前做过,但是忘得差不多了
最后一步什么构造函数的完全没印象,可能因为这玩意实在不常见
于是yy了半天,得到一个可能更为简单的做法,至少不需要构造函数,在这里记录一下
感觉很多yy出来的好东西都没有记录,然后忘了,实在可惜
题解
首先,lcmlcmlcm转gcdgcdgcd方面的前置知识就不再赘述
这个方面似乎没有方法简化了
直接得到模型吧
就是Πf(gcd(ai,ai+1......))(?1)∣T+1∣\Pi f(gcd(a_i,a_{i+1......}))^{(-1)^{}|T+1|}Πf(gcd(ai?,ai+1......?))(?1)∣T+1∣
不难想到一个暴力DP
fi,jf_{i,j}fi,j?表示前i个数,当前的gcdgcdgcd是jjj的带系数的个数和
然后考虑一个iii在不在转移即可
但是这样是O(n2)O(n^2)O(n2)的
考虑h(i)h(i)h(i)表示[1,i][1,i][1,i]里面,枚举的子集当中iii必须选的积
那么gig_igi?显然就是gi?1?hig_{i-1}*h_igi?1??hi?
问题就是h(i)h(i)h(i)怎么求
我们对于一个子集,把他的贡献算在他最大的元素jjj上
设(i,j)=d(i,j)=d(i,j)=d
假如j=dj=dj=d,那么其实加不加入i,对于这个集合的gcd没有任何影响,只不过指数的TTT加了1
于是乘上d(j)d(j)d(j)的倒数即可
否则,我们可以把jjj的子集分为两类,一个是有ddd的一个是没有ddd的,显然这两类个数相同且互为倒数,因此答案是1,也就是不需要转移
于是得到ddd的递推式
d(i)=Πj∣i1d(j)d(i)=\Pi_{j|i}\frac{1}{d(j)}d(i)=Πj∣i?d(j)1?
特殊的,当j=ij=ij=i的时候贡献就是f(i)f(i)f(i)
然后就没有了
代码也很好写
CODE:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
typedef long long LL;
const int N=1000005;
int MOD;
int add (int x,int y) {
x=x+y;return x>=MOD?x-MOD:x;}
int mul (int x,int y) {
return (LL)x*y%MOD;}
int dec (int x,int y) {
x=x-y;return x<0?x+MOD:x;}
int Pow (int x,int y)
{
if (y==0) return 1;if (y==1) return x;int lalal=Pow(x,y>>1);lalal=mul(lalal,lalal);if (y&1) lalal=mul(lalal,x);return lalal;
}
int T;
int n;
int f[N];
int g[N];
int main()
{
freopen("number.in","r",stdin);freopen("number.out","w",stdout);scanf("%d",&T);while (T--){
scanf("%d%d",&n,&MOD);f[1]=1;f[2]=2;for (int u=3;u<=n;u++) f[u]=add(f[u-2],add(f[u-1],f[u-1]));for (int u=1;u<=n;u++) g[u]=f[u];g[0]=1;int ans=0; for (int u=1;u<=n;u++){
int Inv=Pow(g[u],MOD-2);for (int i=u+u;i<=n;i+=u) g[i]=mul(g[i],Inv);g[u]=mul(g[u],g[u-1]);ans=add(ans,mul(u,g[u]));}printf("%d\n",ans);}return 0;
}