题目4 : 有钱就是任性
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
- 样例输入
-
2 2 4
- 样例输出
-
10
描述
俗话说,有钱就是任性。我们的高富帅鱼丸同学打算去看电影。鱼丸到了电影院以后,发现座位的编号正好是1到200。
但是有一些座位号对应的座位坏掉了,没法坐,不妨假设还剩下N个能坐的椅子。电影的老板告诉鱼丸,如果你要包下一个集合S里的所有椅子,就要付出这些椅子的编号的最小公倍数的钱。鱼丸很任性地同意了。
来这里玩了很多天以后,鱼丸发现自己正好来了2N-1天,并且由于他非常任性,对于这N个椅子的每一种可能的非空子集,他都包下过来看电影。鱼丸大少爷虽然不在乎花了多少钱,但你毕竟是他的助理,于是你想知道鱼丸一共花了多少钱。由于钱的数量实在太大,请对答案mod
109+7之后输出。
输入
第一行输入一个数N(1 <= N <= 200),表示能做的椅子的数量。
接下来一行N个1到200之间的整数,用空格隔开。
输出
一行输出答案。
本题是dp,但是map存不下那么大的数(lcm(1,2,...,200)),所以考虑压缩
容易想到≤n的数p>sqrt(n)的只出现一次,
所以可以将它们压缩a1*a2*...*a[m-1]*p=n 压缩为a1*a2*...*a[m-1]
f(i)表示lcm=i的次数
因为让f(a1*a2*...*a[m-1])+=p*f(a1*a2*...*a[m-1]*p)不影响结果就可以了,
值得注意的是f()中的数可能已经超过F了,要先取模以防溢出
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<functional> #include<iostream> #include<cmath> #include<cctype> #include<ctime> #include<map> using namespace std; #define For(i,n) for(int i=1;i<=n;i++) #define Fork(i,k,n) for(int i=k;i<=n;i++) #define Rep(i,n) for(int i=0;i<n;i++) #define ForD(i,n) for(int i=n;i;i--) #define RepD(i,n) for(int i=n;i>=0;i--) #define Forp(x) for(int p=pre[x];p;p=next[p]) #define Forpiter(x) for(int &p=iter[x];p;p=next[p]) #define Lson (x<<1) #define Rson ((x<<1)+1) #define MEM(a) memset(a,0,sizeof(a)); #define MEMI(a) memset(a,127,sizeof(a)); #define MEMi(a) memset(a,128,sizeof(a)); #define INF (2139062143) #define F (1000000007) #define MAXN (2000+10) #define N (200) long long mul(long long a,long long b){return (a*b)%F;} long long add(long long a,long long b){return (a+b)%F;} long long sub(long long a,long long b){return (a-b+(a-b)/F*F+F)%F;} typedef long long ll; void upd(ll &a,ll b){a=(a+b)%F;} int n,a[MAXN],p[MAXN],tot=0; bool b[MAXN]={0}; map<ll,ll> f[2]; ll gcd(ll a,ll b){if (b==0) return a;return gcd(b,a%b);} ll lcm(ll a,ll b){return a/gcd(a,b)*b;} void make_prime(int n) { Fork(i,2,n) { if (!b[i]) p[++tot]=i; For(j,tot) { if (i*p[j]>n) break; b[i*p[j]]=1; if (i%p[j]==0) break; } } } int cnt[MAXN]={0}; ll pw2[MAXN]={1,2,4,8,16,32,64,128,256,512,1024, 2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576, 2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,73741817, 147483634,294967268,589934536,179869065,359738130,719476260,438952513,877905026,755810045,511620083, 23240159,46480318,92960636,185921272,371842544,743685088,487370169,974740338,949480669,898961331, 797922655,595845303,191690599,383381198,766762396,533524785,67049563,134099126,268198252,536396504, 72793001,145586002,291172004,582344008,164688009,329376018,658752036,317504065,635008130,270016253, 540032506,80065005,160130010,320260020,640520040,281040073,562080146,124160285,248320570,496641140, 993282280,986564553,973129099,946258191,892516375,785032743,570065479,140130951,280261902,560523804, 121047601,242095202,484190404,968380808,936761609,873523211,747046415,494092823,988185646,976371285, 952742563,905485119,810970231,621940455,243880903,487761806,975523612,951047217,902094427,804188847, 608377687,216755367,433510734,867021468,734042929,468085851,936171702,872343397,744686787,489373567, 978747134,957494261,914988515,829977023,659954039,319908071,639816142,279632277,559264554,118529101, 237058202,474116404,948232808,896465609,792931211,585862415,171724823,343449646,686899292,373798577, 747597154,495194301,990388602,980777197,961554387,923108767,846217527,692435047,384870087,769740174, 539480341,78960675,157921350,315842700,631685400,263370793,526741586,53483165,106966330,213932660, 427865320,855730640,711461273,422922539,845845078,691690149,383380291,766760582,533521157,67042307, 134084614,268169228,536338456,72676905,145353810,290707620,581415240,162830473,325660946,651321892, 302643777,605287554,210575101,421150202,842300404,684600801,369201595,738403190,476806373,953612746, 907225485,814450963,628901919,257803831,515607662,31215317,62430634,124861268,249722536,499445072 }; bool use[MAXN]={0}; int main() { // freopen("hiho1113.in","r",stdin); // freopen("hiho1113.out","w",stdout); make_prime(200); cin>>n; For(i,n) cin>>a[i],cnt[a[i]]++; bool ct=0; f[0][1]=1; //假设一开始有一个空集 lcm(φ)=1的 ,显然lcm(φ,a,b,c..)=lcm(a,b,c..) // 后面 则是 {解集}+元素i ForD(j,tot) if (p[j]*p[j]>N) //若p[j]*k=N=200 则 k<sqrt(N)<p[j] { for(int k=1;k<=N/p[j];k++) { int i=k*p[j];use[i]=1; if (!cnt[i]) continue; for(map<ll,ll>::iterator it=f[ct].begin();it!=f[ct].end();it++) { ll c=it->first,v=it->second; upd(f[ct^1][c],f[ct][c]); //有可能之前在lcm时改过 upd(f[ct^1][lcm(c,i)],mul(f[ct][c],sub(pw2[cnt[i]],1))); } f[ct].clear(); ct^=1; } //显然p[j]最多 出现1次,且之后不出现,故 把p[j]"删"去不对答案影响 f(i)表示 lcm(..)=i的数的个数 //eg cost=2*f(2)+2*56*f(2*56) = 2*F(2) F(2)=(f(2)+56*f(2*56)) //添加17 -> F(2*17)=f(2*17)+56*f(2*17*56) for(map<ll,ll>::iterator it=f[ct].begin();it!=f[ct].end();it++) { ll c=it->first,v=it->second; if (c%p[j]==0) upd(f[ct^1][c/p[j]],mul(v,p[j])); else upd(f[ct^1][c],v); } f[ct].clear(); ct^=1; } For(i,N) { if (use[i]) continue; if (!cnt[i]) continue; for(map<ll,ll>::iterator it=f[ct].begin();it!=f[ct].end();it++) { ll c=it->first,v=it->second; upd(f[ct^1][c],f[ct][c]); //有可能之前在lcm时改过 upd(f[ct^1][lcm(c,i)],mul(f[ct][c],sub(pw2[cnt[i]],1))); } f[ct].clear(); ct^=1; } ll ans=0; for(map<ll,ll>::iterator it=f[ct].begin();it!=f[ct].end();it++) upd(ans,mul((it->first)%F,(it->second)%F)); cout<<sub(ans,1)<<endl; return 0; }
时间: 2024-12-07 02:25:54