TopcoderSRM748div1-250

博客围绕一个序列问题展开,要通过擦除部分元素使序列的最大公约数为目标值,并计算实现方式的数量。介绍了使用容斥原理和反演两种方法求解,容斥法设f[i]、g[i]等,复杂度为O(nlogn),反演法也给出了相应公式,但作者未写代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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]为答案,即gcdgcdgcdiii的组合有多少个,这个东西不太好求。
根据套路,要大力容斥。
g[i]g[i]g[i]gcdgcdgcdiii的倍数的组合的个数
那么有
f[i]=g[i]−∑i∣d且i!=dg[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]为数字为iii数字有多少个,h[i]h[i]h[i]为数字为iii的倍数的数字有多少个。
那么有h[i]=∑i∣da[d]h[i]=\sum_{i|d}a[d]h[i]=ida[d]
这样就有g[i]=2h[i]−1g[i]=2^{h[i]}-1g[i]=2h[i]1
像埃筛一样枚举,总复杂度为O(nlogn)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[gcd{S}==i]f[i]=\sum_{S}[gcd{\{S\}} ==i]f[i]=S[gcd{S}==i]
S‘S`SSSS中的每个元素除以iii
f[i]=∑S‘[gcd{S‘}==1]f[i]=\sum_{S`}[gcd\{S`\}==1]f[i]=S[gcd{S}==1]
然后大力反演
f[i]=∑S‘∑d∣gcd{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、付费专栏及课程。

余额充值