[bzoj4635]数论小测验 解题报告
2016-07-05 11:23
260 查看
感觉自己好蠢只会反演。。跑了整整10s。。
ans=∑k=lr∑i=1⌊mk⌋μ(i)⌊⌊mk⌋i⌋n
这样时间复杂度就是O(∑m√i=1i√+∑m√i=1mi−−√)=O(m34).我还化来化去化了半天式子。。结果a了之后才发现原始的式子直接做就可以了。。
这样其实应该是O(m34logn)的,所以我们还需要预处理一下后面的幂,就可以做到O(T(m34+m−−√logn))了。
设f(k,x)表示[1,m]中与k的gcd是x的数的个数,则
ans=∑k=lr(mn−∑d|kf(k,d)μ(kd)[d≠k])
按m从小到大离线处理询问,这样就可以处理f,花费O(m2logm),每次询问是O(mlogm),所以总时间复杂度是O(m2logm+TlogT+Tmlogm)
然后看了claris老司机的题解,感觉好神啊。。原来第一问可以直接考虑反面dp来搞,第二问可以先取反再容斥。
代码:
总结:
①两个根号枚举套一起是O(n34)的!
②直接dp确定值考虑反面是很方便的。如果把这种dp展开会发现是个容斥,但是如果直接从容斥的角度考虑就会变得非常麻烦了(因为会有什么至少、至多这种奇怪的东西)。
③虽然反演是一种容斥,但是有的时候不一定需要它,强行上反演反而会变得麻烦的。考虑一下应该具体怎么容斥。
ans=∑k=lr∑i=1⌊mk⌋μ(i)⌊⌊mk⌋i⌋n
这样时间复杂度就是O(∑m√i=1i√+∑m√i=1mi−−√)=O(m34).我还化来化去化了半天式子。。结果a了之后才发现原始的式子直接做就可以了。。
这样其实应该是O(m34logn)的,所以我们还需要预处理一下后面的幂,就可以做到O(T(m34+m−−√logn))了。
设f(k,x)表示[1,m]中与k的gcd是x的数的个数,则
ans=∑k=lr(mn−∑d|kf(k,d)μ(kd)[d≠k])
按m从小到大离线处理询问,这样就可以处理f,花费O(m2logm),每次询问是O(mlogm),所以总时间复杂度是O(m2logm+TlogT+Tmlogm)
然后看了claris老司机的题解,感觉好神啊。。原来第一问可以直接考虑反面dp来搞,第二问可以先取反再容斥。
代码:
#include<cstdio> #include<iostream> using namespace std; #include<cstring> #include<cmath> #include<algorithm> const int T=500+5,N=1e7+5; const int Mod=1e9+7; int t; typedef long long LL; LL power(LL prod,int x){ LL ans=1; for(;x;x>>=1){ if(x&1)ans=ans*prod%Mod; prod=prod*prod%Mod; } return ans; } namespace N1{ const int M=1e7; bool p[M+5]; int prime[M]; int mu[M+5]; int smu[M+5]; const int R=5000+5; LL pwr1[R],pwr2[R]; void work(){ mu[1]=1; for(int i=2;i<=M;++i){ if(!p[i]){ prime[++prime[0]]=i; mu[i]=-1; } for(int j=1;j<=prime[0]&&i*prime[j]<=M;++j){ p[i*prime[j]]=1; if(i%prime[j])mu[i*prime[j]]=-mu[i]; else break; } } for(int i=1;i<=M;++i)smu[i]=smu[i-1]+mu[i]; while(t--){ int n,m,l,r; scanf("%d%d%d%d",&n,&m,&l,&r); int root=sqrt(m); for(int i=root;i;--i){ pwr1[i]=power(i,n); pwr2[i]=power(m/i,n); } LL ans=0; for(int i=1,j,tmp=m/i;i<=m&&tmp>=l;i=j+1,tmp=m/i){ j=m/tmp; //printf("---[%d,%d]---\n",i,j); LL s=0; for(int k=l,o;k<=min(tmp,r);k=o+1){ o=min(tmp/(tmp/k),r); if(tmp/k<=root)s=(s+(o-k+1)*pwr1[tmp/k])%Mod; else s=(s+(o-k+1)*pwr2[i*k])%Mod; //printf("[%d,%d]=%I64d\n",k,o,(o-k+1)*power(tmp/k,n)%Mod); } ans=(ans+s*(smu[j]-smu[i-1]))%Mod; } printf("%lld\n",(ans+Mod)%Mod); } } } #include<vector> namespace N2{ const int M=1000; int cnt[M+5][M+5]; bool p[M+5]; int prime[M]; int mu[M+5]; int gcd[M+5][M+5]; struct QS{ int n,m,l,r,i; bool operator < (const QS & o)const{ return m<o.m; } }que[T]; LL ans[T]; void work(){ mu[1]=1; for(int i=2;i<=M;++i){ if(!p[i]){ prime[++prime[0]]=i; mu[i]=-1; } for(int j=1;j<=prime[0]&&i*prime[j]<=M;++j){ p[i*prime[j]]=1; if(i%prime[j])mu[i*prime[j]]=-mu[i]; else break; } } for(int i=M;i;--i)gcd[i][0]=gcd[0][i]=i; for(int i=1;i<=M;++i) for(int j=1;j<=i;++j) gcd[i][j]=gcd[j][i]=gcd[j][i%j]; for(int i=1;i<=t;++i){ scanf("%d%d%d%d",&que[i].n,&que[i].m,&que[i].l,&que[i].r); que[i].i=i; } sort(que+1,que+t+1); for(int i=1,j=0;i<=t;++i){ for(;j<=que[i].m;++j) for(int o=M>>1;o;--o) for(int k=o<<1;k<=M;k+=o) if(mu[k/o]&&o%gcd[k][j]==0) ++cnt[k][o]; ans[que[i].i]=(que[i].r-que[i].l+1)*power(que[i].m,que[i].n)%Mod; for(int o=1;o<=que[i].r>>1;++o) for(int k=o<<1;k<=que[i].r;k+=o) if(k>=que[i].l&&mu[k/o]) ans[que[i].i]=(ans[que[i].i]+mu[k/o]*power(cnt[k][o],que[i].n))%Mod; } for(int i=1;i<=t;++i)printf("%lld\n",(ans[i]+Mod)%Mod); } } int main(){ freopen("bzoj_4635.in","r",stdin); //freopen("bzoj_4635.out","w",stdout); int type; scanf("%d%d",&t,&type); if(type==1)N1::work(); else N2::work(); }
总结:
①两个根号枚举套一起是O(n34)的!
②直接dp确定值考虑反面是很方便的。如果把这种dp展开会发现是个容斥,但是如果直接从容斥的角度考虑就会变得非常麻烦了(因为会有什么至少、至多这种奇怪的东西)。
③虽然反演是一种容斥,但是有的时候不一定需要它,强行上反演反而会变得麻烦的。考虑一下应该具体怎么容斥。
相关文章推荐
- 图片圆角处理封装
- 推荐系统概论
- maven 备用
- kafka 之 producer篇
- webview
- B站直播 DEMO ijkplayerDemo
- 博客里程
- Templates
- 例题8-12 UVA - 12627 Erratic Expansion 奇怪的气球膨胀 (递归)
- kafka 的network
- 拨开kafka 的羊毛衫
- ProgressDialog使用
- shell 判断 404
- AES对称加密
- 101. Symmetric Tree
- Route Filters
- Routes
- Android中的布局和控件的隐藏和触发显示
- java 内存模型与线程 正文
- AndroidStudio如何打包生成realease版本的arr包,并上传到Nexus搭建的maven仓库,供项目远程依赖(二)