TopcoderSRM748div1-250

Problem Statement

You have a sequence S of small positive integers. You want to erase some (possibly none but not all) elements of S in such a way that the greatest common divisor of the resulting sequence becomes exactly goal.
Let W be the number of ways in which the above can be done. Compute and return the value (W modulo (10^9 + 7)).


Definition

Class: EraseToGCD
Method: countWays
Parameters: vector , int
Returns: int
Method signature: int countWays(vector S, int goal)
(be sure your method is public)


Limits

Time limit (s): 2.000
Memory limit (MB): 256


Notes

Two ways of erasing are different if the sets of indices of erased elements differ.


Constraints

S will have between 1 and 500 elements, inclusive.

Each element of S will be between 1 and 1000, inclusive.

goal will be between 1 and 1000, inclusive.


为什么这样的套路题还可以原封不动的出出来。
f [ i ] f[i] f[i]为答案,即 g c d gcd gcd i i i的组合有多少个,这个东西不太好求。
根据套路,要大力容斥。
g [ i ] g[i] g[i] g c d gcd gcd i i i的倍数的组合的个数
那么有
f [ i ] = g [ i ] − ∑ i ∣ d 且 i ! = d g [ d ] f[i]=g[i]-\sum_{i|d 且 i != d}g[d] f[i]=g[i]idi!=dg[d]
g [ i ] g[i] g[i]就比 f [ i ] f[i] f[i]好求了。
a [ i ] a[i] a[i]为数字为 i i i数字有多少个, h [ i ] h[i] h[i]为数字为 i i i的倍数的数字有多少个。
那么有 h [ i ] = ∑ i ∣ d a [ d ] h[i]=\sum_{i|d}a[d] h[i]=ida[d]
这样就有 g [ i ] = 2 h [ i ] − 1 g[i]=2^{h[i]}-1 g[i]=2h[i]1
像埃筛一样枚举,总复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2000;
const int mod = 1e9+7;
typedef long long ll;
class EraseToGCD{
	public:
		ll pw(ll x,ll y){
			ll ret = 1;
			while(y){
				if(y&1)ret=(ret*x)%mod;
				x=(x*x)%mod;
				y>>=1;
			}
			return ret;
		}
		ll f[MAXN];
		int countWays(vector <int> S, int goal){
			memset(f,0,sizeof f);
			for(int i=0;i<(int)S.size();i++){
				f[S[i]]++;
			}
			for(int i=1;i<2000;i++){
				for(int j=i*2;j<2000;j+=i){
					f[i] += f[j];
				}
			}
		//	cout<<f[6]<<endl;
			for(int i=1;i<2000;i++){
				f[i]=pw(2,f[i]);
				f[i]--;
				if(f[i]<0)
					f[i]+=mod;
			}//cerr<<"here"<<endl;
			
			for(int i=1999;i>0;i--){
				for(int j=i*2;j<2000;j+=i){
					f[i] -= f[j];
					f[i]%=mod;
					if(f[i]<0)
						f[i]=(f[i]+mod)%mod;
				}
			}
			return f[goal];
		}
};

/*
int n=5,data[]={0,6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 15, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 10, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,0},g=6;
vector<int> arr;
int main(){
	for(int i=1;data[i]!=0;i++)
		arr.push_back(data[i]);
	EraseToGCD t;
	cout<<t.countWays(arr,g);
	return 0;
}*/

PS

当然可以反演啦。
f [ i ] f[i] f[i]为答案,S为枚举的集合

f [ i ] = ∑ S [ g c d { S } = = i ] f[i]=\sum_{S}[gcd{\{S\}} ==i] f[i]=S[gcd{S}==i]
S ‘ S` S S S S中的每个元素除以 i i i
f [ i ] = ∑ S ‘ [ g c d { S ‘ } = = 1 ] f[i]=\sum_{S`}[gcd\{S`\}==1] f[i]=S[gcd{S}==1]
然后大力反演
f [ i ] = ∑ S ‘ ∑ d ∣ g c d { S ‘ } μ ( d ) f[i]=\sum _{S`}\sum_{d|gcd{\{S`\}}}\mu(d) f[i]=Sdgcd{S}μ(d)
参考上面的 g [ i ] g[i] g[i]的定义

f [ i ] = ∑ d μ ( d ) ∗ g ( i ∗ d ) f[i]=\sum_{d}\mu(d)*g(i*d) f[i]=dμ(d)g(id)
变换一下枚举元素
f [ i ] = ∑ i ∣ j μ ( j / i ) ∗ g ( j ) f[i]=\sum_{i|j}\mu(j/i)*g(j) f[i]=ijμ(j/i)g(j)
和上面一样求 g [ i ] g[i] g[i]就好了。
然而太懒了,没写代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值