NOIP模拟(20171024)T2 乘积
2017-10-24 19:57
507 查看
求从1-n中选k个数,使得这k个数的乘积不含完全平方因子
70% n≤30
100% n≤500
15bbf
nobr>pos[i+1]
f[i][j][k]→f[i+1][j+1][k|pos[i+1]](k&pos[i+1]=0)
f[i][j][k]→f[i+1][j][k]
(考场代码,求轻喷)
证明当n=500时至多取96个数使它们满足题意
取500中所有质数及1,共96个数,满足题意
下证97及以上不可能
构造抽屉:
{1}
{2,4,6,8,10,⋯,500}
{3,6,9,12,⋯,498}
{5,10,15,⋯,500}
⋮
{491}
{499}
共96个
当k≥97 时一个抽屉里至少有两个数
明显当一个抽屉中有至少两个数被选后,乘积一定包含完全平方因子
故kmax=96
(说了那么多废话,就是想吐槽n=500,k≥400 的数据是要闹哪样)
好了,抽屉摆在这里,明显,每个抽屉里只能选一个数
那么答案就是∏ 集合大小(了吗)
naive
有些数(比如6)出现在了不止一个集合中,这样计算肯定是错的
那就让一个数只在一个集合里
……
……
……
(woc,做不到啊)
一个显然的性质:一个数x至多含有一个质因数大于x√
好 重新分组
{23,46,69,⋯,483}
{29,58,87,⋯,493}
⋮
{491}
{499}
这样就可以(了吗×2)
还有些数没有呢!
其余数全部一个数一个桶
这样每个桶选一个后,大于n√ 的质因数至多只有一个
只用考虑小于的
用70%的dp
然后MLE了
用滚动数组,或直接参照0-1背包的状态优化
详见代码
70% n≤30
100% n≤500
70%:
状压dp,f[i][j][k]表示前i个数,选了j个,当前所选数之积分解质因数后的状态为k(k表示成二进制后,第一位表示有没有2,第二位表示有没有3……以此类推)令i+1分解质因数后状态为<15bbf
nobr>pos[i+1]
f[i][j][k]→f[i+1][j+1][k|pos[i+1]](k&pos[i+1]=0)
f[i][j][k]→f[i+1][j][k]
#include<bits/stdc++.h> #define MOD 1000000007 using namespace std; inline int getint(){ int x=0,p=1; char c=getchar(); while(!isdigit(c)){ if(c=='-')p=-1; c=getchar(); } while(isdigit(c)){ x=(x<<3)+(x<<1)+(c^'0'); c=getchar(); } return x*p; } int primes[505],tot; bool pd[505]; inline void pre(){ pd[1]=1; for(int i=2;i<=500;++i){ if(!pd[i]){ primes[++tot]=i; } for(int j=1;j<=tot&&i*primes[j]<=500;++j){ pd[i*primes[j]]=1; if(i%primes[j]==0)break; } } } inline void putint(long long x){ if(x<0){ x=-x; putchar('-'); } static long long buf[22]; long long tot=0; do{ buf[tot++]=x%10; x/=10; }while(x); while(tot)putchar(buf[--tot]+'0'); } long long f[35][13][2050]; long long sum[35][13]; inline void predp(){ f[0][0][0]=1; for(int i=0;i<=29;++i){ int x=0; bool flag=0; for(int j=1;j<=10;++j){ if((i+1)%primes[j]==0){ x^=1<<(j-1); int t=(i+1)/primes[j]; if(t%primes[j]==0){ flag=1; break; } } } //cout<<i+1<<" "<<x<<endl; for(int j=0;j<=11;++j){ for(int k=0;k<1024;++k){ f[i+1][j][k]+=f[i][j][k]; f[i+1][j][k]%=MOD; if((k&x)==0&&(!flag))(f[i+1][j+1][k|x]+=f[i][j][k])%=MOD; } } } // for(int i=1;i<=6;++i){ // for(int j=1;j<=i;++j){ // for(int k=0;k<8;++k){ // cout<<i<<" "<<j<<" "<<k<<" "<<f[i][j][k]<<endl; // } // } // } for(int i=0;i<=30;++i){ for(int j=0;j<=11;++j){ sum[i][j]=0; for(int k=0;k<=1024;++k){ sum[i][j]+=f[i][j][k]; sum[i][j]%=MOD; } } } } inline void work1(int n,int k){ if(k>12)k=12; long long ans=0; for(int i=1;i<=k;++i){ ans+=sum [i]; ans%=MOD; } cout<<ans<<endl; } inline void work(int n,int k){ if(n<=30)work1(n,k); else putint(rand()),putchar('\n'); } int main(){ pre(); predp(); int t=getint(); while(t--){ int n=getint(),k=getint(); work(n,k); } return 0; }
(考场代码,求轻喷)
100%
先来做一道小学题:证明当n=500时至多取96个数使它们满足题意
取500中所有质数及1,共96个数,满足题意
下证97及以上不可能
构造抽屉:
{1}
{2,4,6,8,10,⋯,500}
{3,6,9,12,⋯,498}
{5,10,15,⋯,500}
⋮
{491}
{499}
共96个
当k≥97 时一个抽屉里至少有两个数
明显当一个抽屉中有至少两个数被选后,乘积一定包含完全平方因子
故kmax=96
(说了那么多废话,就是想吐槽n=500,k≥400 的数据是要闹哪样)
好了,抽屉摆在这里,明显,每个抽屉里只能选一个数
那么答案就是∏ 集合大小(了吗)
naive
有些数(比如6)出现在了不止一个集合中,这样计算肯定是错的
那就让一个数只在一个集合里
……
……
……
(woc,做不到啊)
一个显然的性质:一个数x至多含有一个质因数大于x√
好 重新分组
{23,46,69,⋯,483}
{29,58,87,⋯,493}
⋮
{491}
{499}
这样就可以(了吗×2)
还有些数没有呢!
其余数全部一个数一个桶
这样每个桶选一个后,大于n√ 的质因数至多只有一个
只用考虑小于的
用70%的dp
然后MLE了
用滚动数组,或直接参照0-1背包的状态优化
详见代码
#include<bits/stdc++.h> #define FE "mul" #define MOD 1000000007 using namespace std; inline int getint(){ int x=0,p=1; char c=getchar(); while(!isdigit(c)){ if(c=='-')p=-1; c=getchar(); } while(isdigit(c)){ x=(x<<3)+(x<<1)+(c^'0'); c=getchar(); } return x*p; } int primes[505],tot; bool pd[505]; inline void pre(){ pd[1]=1; for(int i=2;i<=500;++i){ if(!pd[i]){ primes[++tot]=i; } for(int j=1;j<=tot&&i*primes[j]<=500;++j){ pd[i*primes[j]]=1; if(i%primes[j]==0)break; } } } inline void putint(long long x){ if(x<0){ x=-x; putchar('-'); } static long long buf[22]; long long tot=0; do{ buf[tot++]=x%10; x/=10; }while(x); while(tot)putchar(buf[--tot]+'0'); } int dp[100][260]; vector<int>bucket[505]; int pos[505]; inline void add(int &a,int b){ a+=b; a>=MOD?a-=MOD:0; } inline void work2(int n,int k){ if(k>98)k=98; int fi=1; for(int i=1;i<=n;++i){ bucket[i].clear(); pos[i]=0; } while(primes[fi]<sqrt(n)){ ++fi; } for(int i=1;i<=n;++i){ int temp=i; for(int j=1;j<fi;++j){ int v=primes[j]; if(temp%(v*v)==0){ pos[i]=-1; break; } if(temp%v==0){ pos[i]^=(1<<(j-1)); temp/=v; } } if(~pos[i]){ if(temp!=1){ bucket[temp].push_back(i); } else{ bucket[i].push_back(i); } } } memset(dp,0,sizeof(dp)); dp[0][0]=1; for(int i=1;i<=n;++i){ if(bucket[i].size()){ int sz=bucket[i].size(); for(int j=k;j>=0;--j){ for(int l=0;l<sz;++l){ for(int m=pos[bucket[i][l]],p=255^m; ;p=((p-1)&(255^m))){ add(dp[j+1][p|m], dp[j][p]); if(p==0)break; } } } } } int ans=0; for(int i=1;i<=k;++i){ for(int j=0;j<256;++j){ add(ans,dp[i][j]); } } putint(ans),putchar('\n'); } inline void work(int n,int k){ work2(n,k); } int main(){ pre(); int t=getint(); while(t--){ int n=getint(),k=getint(); work(n,k); } return 0; }
相关文章推荐
- NOIP模拟(10.24)T2 乘积
- NOIP模拟(20171023)T2 一样远
- NOIP模拟(10.30)T2 Game
- [NOIp2000 T2] 乘积最大(序列dp)
- NOIP模拟(11.02)T2 最佳序列
- NOIP模拟(20171102)T2 最佳序列
- NOIP模拟(10.22)T2 杆子的排列
- NOIP模拟(11.06)T2 序列操作
- NOIP模拟(10.20)T2 矩阵(bzoj1084 最大子矩阵)
- NOIP模拟(10.31)T2 朋友 (bzoj2143 飞飞侠)
- 洛谷 P1018 [NOIP2000 T2] 乘积最大
- NOIP模拟(10.23)T2 一样远
- NOIP模拟(20171026)T2 做运动
- NOIP模拟(20171030)T2 游戏
- NOIP模拟(11.03)T2 排列
- NOIP模拟(11.07)T2 路径统计
- 磁共振T1 T2 T1WI T2WI含义
- 构造无穷个无穷小量,使它们的乘积为常数
- T2 Func<in T1,out T2>(T1 arg)
- 【DFS序】【线段树】bzoj4034 [HAOI2015]T2