UPD:感觉这个应该不叫容斥原理…
又是一道神奇的题
最开始就想到容斥,结果写完了之后过不了样例,意识到我那个容斥好像并不满足容斥那个组合数的条件。。
然后标算的容斥刷新了我对容斥的认识= =
先预处理出
d
p
[
i
]
dp[i]
dp[i],表示长度为
i
i
i的不合法序列数(
d
p
[
1
]
dp[1]
dp[1]事实上是合法的,但还是把它算出来),这里说的不合法是指每对相邻元素都不合法,即每个数都整除它前一个数(其实前后都无所谓啦)。
我们用
h
[
i
]
h[i]
h[i]表示长度为
i
i
i的合法序列个数,考虑我们在
h
[
i
−
1
]
h[i-1]
h[i−1]最后再加一个数,总方案数为
h
[
i
−
1
]
×
d
p
[
1
]
h[i-1]×dp[1]
h[i−1]×dp[1],但这个数可能整除前一个数,我们们把这些情况减去,就是
−
h
[
i
−
2
]
×
d
p
[
2
]
-h[i-2]×dp[2]
−h[i−2]×dp[2],然后会发现,好像会把后三个数不合法的情况多减,所以又
+
h
[
i
−
3
]
×
d
p
[
3
]
+h[i-3]×dp[3]
+h[i−3]×dp[3]…好吧,感觉我好像没怎么讲清楚,语言表达能力有限,最好手模一下。
代码很好懂。
#include <bits/stdc++.h>
#define ll long long
#define fr(i,x,y) for(int i=x;i<=y;i++)
#define rf(i,x,y) for(int i=x;i>=y;i--)
using namespace std;
const int p=1e9+7;
const int N=50001;
const int Lg=17;
int f[N][Lg],dp[Lg];
ll h[N];
template<class T> void checkmin(T &a,const T &b) { if (b<a) a=b; }
template<class T> void checkmax(T &a,const T &b) { if (b>a) a=b; }
class DivFree {
public:
int dfcount( int n, int k ) ;
};
void Add(int &x,int y){
x+=y;
while(x<0) x+=p;
while(x>=p) x-=p;
}
void Add(ll &x,ll y){
x+=y;
while(x<0) x+=p;
while(x>=p) x-=p;
}
int DivFree::dfcount(int n, int K) {
//init(K);
fr(i,1,K) f[i][1]=1;
for(int j=1;j<Lg;j++)
for(int i=1;i<=K;i++)
if (f[i][j]){
for(int k=2*i;k<=K;k+=i)
Add(f[k][j+1],f[i][j]);
Add(dp[j],f[i][j]);
}
h[0]=1;
fr(i,1,n)
for(int j=1,v=1;j<Lg&&j<=i;j++,v=-v)
Add(h[i],v*h[i-j]*dp[j]%p);
return h[n];
}