前言
上午模拟赛考了这个题
虽然之前做过,但是忘得差不多了
最后一步什么构造函数的完全没印象,可能因为这玩意实在不常见
于是yy了半天,得到一个可能更为简单的做法,至少不需要构造函数,在这里记录一下
感觉很多yy出来的好东西都没有记录,然后忘了,实在可惜
题解
首先,
l
c
m
lcm
lcm转
g
c
d
gcd
gcd方面的前置知识就不再赘述
这个方面似乎没有方法简化了
直接得到模型吧
就是
Π
f
(
g
c
d
(
a
i
,
a
i
+
1......
)
)
(
−
1
)
∣
T
+
1
∣
\Pi f(gcd(a_i,a_{i+1......}))^{(-1)^{}|T+1|}
Πf(gcd(ai,ai+1......))(−1)∣T+1∣
不难想到一个暴力DP
f
i
,
j
f_{i,j}
fi,j表示前i个数,当前的
g
c
d
gcd
gcd是
j
j
j的带系数的个数和
然后考虑一个
i
i
i在不在转移即可
但是这样是
O
(
n
2
)
O(n^2)
O(n2)的
考虑
h
(
i
)
h(i)
h(i)表示
[
1
,
i
]
[1,i]
[1,i]里面,枚举的子集当中
i
i
i必须选的积
那么
g
i
g_i
gi显然就是
g
i
−
1
∗
h
i
g_{i-1}*h_i
gi−1∗hi
问题就是
h
(
i
)
h(i)
h(i)怎么求
我们对于一个子集,把他的贡献算在他最大的元素
j
j
j上
设
(
i
,
j
)
=
d
(i,j)=d
(i,j)=d
假如
j
=
d
j=d
j=d,那么其实加不加入i,对于这个集合的gcd没有任何影响,只不过指数的
T
T
T加了1
于是乘上
d
(
j
)
d(j)
d(j)的倒数即可
否则,我们可以把
j
j
j的子集分为两类,一个是有
d
d
d的一个是没有
d
d
d的,显然这两类个数相同且互为倒数,因此答案是1,也就是不需要转移
于是得到
d
d
d的递推式
d
(
i
)
=
Π
j
∣
i
1
d
(
j
)
d(i)=\Pi_{j|i}\frac{1}{d(j)}
d(i)=Πj∣id(j)1
特殊的,当
j
=
i
j=i
j=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;
}