bzoj 3930: [CQOI2015]选数【递推】

妙啊
这个题一上来就想的是莫比乌斯反演:

\[f(d)=\sum_{k=1}^{\left \lceil \frac{r}{d} \right \rceil}\mu(k)(\left \lceil \frac{r}{kd} \right \rceil-\left \lceil \frac{l-1}{kd} \right \rceil)^n \]

但是看到r的范围发现前缀和不能处理于是不能分块于是时间复杂度为\( O(rlog_2n) \)于是GG。(其实是可以处理的但是我不会比较麻烦,而且复杂度高所以没写,方法详见PoPoQQQ大爷blog http://blog.csdn.net/popoqqq/article/details/44917831)

这时注意到r-l的范围看起来很可做,所以考虑复杂度与len有关的算法。

这里有一个性质,当a集合不全部相等时\( gcd(a_1,a_2...,a_n)=k,k\leq a_{max}-a_{min} \)。
证明:设\( {b_1,b_2...,b_n}={\frac {a_1} {k},\frac {a_2} {k},...,\frac {a_n} {k},} \),因为不全部相等,所以\( b_{max}-b_{min} \geq 1 \),所以\( (b_{max}-b_{min})*k=a_{max}-a_{min}\geq k \)

首先,\( l=(l-1)/k,r=r/k \)把问题转换为在新的\( (l,r) \)范围内求\( gcd==1 \) 的方案数(注意以下的l和r都是新的范围);
设\( f[i] \)为gcd为i时的方案数,可以求出gcd为i的倍数是的方案数\( sum=(\left \lceil r/i \right \rceil-\left \lceil l/i \right \rceil)^n-(\left \lceil r/i \right \rceil-\left \lceil l/i \right \rceil) \),减去的是集合内数字全部相等的方案个数。那么\( f[i]=sum - \sum_{i|j}^{j\leq len} f[j] \)。可以选择从后往前递推。

时间复杂度...理论上是\( O(lenlog_2len) \)或者 \( O(lenlog_2n) \),但是调和级数和快速幂的复杂度我不知道怎么加(躺

#include<iostream>
#include<cstdio>
using namespace std;
const int N=100005,mod=1e9+7;
int n,k,l,r,len,p,f[N];
int ksm(int a,int b)
{
	int r=1;
	while(b)
	{
		if(b&1)
			r=(long long)r*a%mod;
		a=(long long)a*a%mod;
		b>>=1;
	}
	return r;
}
int main()
{
	scanf("%d%d%d%d",&n,&k,&l,&r);
	if(l<=k&&r>=k)
		p=1;
	l=(l-1)/k,r=r/k,len=r-l;
	for(int i=len;i>=1;i--)
	{
		int x=l/i,y=r/i;
		f[i]=(ksm(y-x,n)-y+x+mod)%mod;//减去的是区间全部相等
		for(int j=i*2;j<=len;j+=i)
			f[i]=((f[i]-f[j])%mod+mod)%mod;
	}
	printf("%d\n",f[1]+p);
	return 0;
}
posted @ 2018-01-10 11:01  lokiii  阅读(90)  评论(0编辑  收藏  举报