CF900D Unusual Sequences

CF900D Unusual Sequences
一开始看数据范围好像没什么思路
后来机房大佬提醒可以反演
首先如果y不是x的倍数,那么直接输出0
不然就 y /= x,求gcd为1的序列个数
设 g ( x ) 为 和 为 x , 没 有 限 制 的 序 列 个 数 设g(x)为和为x,没有限制的序列个数 g(x)x
x 个 1 , 有 x − 1 个 空 , 即 序 列 个 数 g ( x ) = 2 x − 1 x个1,有x-1个空,即序列个数g(x)=2^{x-1} x1x1g(x)=2x1
再 设 f ( x ) 为 和 为 x , g c d 为 1 的 序 列 个 数 再设f(x)为和为x,gcd为1的序列个数 f(x)xgcd1
显 然 g ( x ) = ∑ d ∣ x f ( d ) 显然g(x)=\sum\limits_{d|x}f(d) g(x)=dxf(d)
这个随便想想gcd就能推出来
这一步可以直接反演,也可以移一下项,然后继续推
f ( x ) = g ( x ) − ∑ d ∣ x , d > 1 f ( x / d ) f(x)=g(x) - \sum\limits_{d|x, d > 1}f(x / d) f(x)=g(x)dx,d>1f(x/d)
然后直接递推即可
然而递推的复杂度不太会算
好像是 O ( d ( n ) l o g n ) O(d(n)logn) O(d(n)logn)

#include<bits/stdc++.h>
#define mod 1000000007
#define int long long
using namespace std;
int qpow(int x, int y) {
	int ret = 1;
	for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
	return ret;
}
map<int, int> ff;
int f(int n) {
	if(n == 1) return 1;
	if(ff[n]) return ff[n];
	ff[n] = qpow(2, n - 1);
	ff[n] = (ff[n] - f(1) + mod) % mod;
	for(int i = 2; i * i <= n; i ++) if(n % i == 0) {
		ff[n] = (ff[n] - f(i) + mod) % mod;
		if(i * i  != n)
			ff[n] = (ff[n] - f(n / i) + mod) % mod;
	}
	return ff[n];
}
int x, y;
signed main() {
	scanf("%lld%lld", &x, &y);
	if(y % x != 0) printf("0");
	else {
		y /= x; 
		printf("%lld", f(y));
	}
	return 0;
}

这种套路还是挺常见的

posted @ 2020-11-06 07:42  lahlah  阅读(30)  评论(0编辑  收藏  举报