CodeForces 594D REQ(树状数组)
2015-11-27 12:38
531 查看
分析:第一反应是用莫队来写,这样每次查询的复杂度是o(n∗n−−√)o(n*\sqrt{n}),设MM是a[i]a[i]中最大的数,那么每一次修改的复杂度是o(logM)o(logM),那么总的复杂度是o(n∗n−−√∗logM)o(n*\sqrt{n}*logM),这样的复杂度给人的感觉应该是要TT了,交上去确实也T了,如果nn是10410^4级别应该就没什么问题了。
感觉这个可以作为莫队算法在一类问题上的优化,因为每一次都是查询[l,r][l,r]这个区间所有不同的质数,最终的答案是
∏i=lra[i]∗∏p−1p\prod_{i=l}^{r}a[i]*\prod{{p-1}\over{p}}
看这个式子,前面一块我们可以维护前缀积,每次查询o(1)o(1)就可以搞定,后面一块要找出区间[l,r][l,r]中所有的不同的质数的函数的乘积,用莫队平均每次查找的复杂度是o(n−−√)o(\sqrt{n}),这个地方能不能优化?
当固定左端点ll的时候,把[l,n][l,n]这段区间所有的质数第一次出现的位置记录下来,把所有出现的质数扔到他们第一次出现的位置上面,用线段树维护的话,每一次查询的复杂度就是$o(logn)。
现在需要解决的是l→l+1l\to{l+1}这个时候线段树维护的区间变成了[l+1,n][l+1,n],即把a[l]a[l]的影响去掉,就是把线段是中ll的位置的乘积变成11(这个地方我写的时候考虑复杂了),那么假如p|a[l]p|a[l]这个时候把pp去掉了,[l+1,n][l+1,n]中仍然有pp的倍数,这个时候要把右边第一个出现pp的倍数的位置的中对应的线段树的位置乘上p−1p{p-1}\over{p},这题就做完了。
复杂度:预处理所有质因数,并且每个数所含有的质数o(M∗logM)o(M*logM),扫一遍[1,n][1,n]中所有能整除的质数的位置o(n∗logM)o(n*logM),树状数组查找o(n∗logn∗logM)o(n*logn*logM)
总的复杂度就是o(M∗logM)+o(n∗logn∗logM)o(M*logM)+o(n*logn*logM)
代码:(自己写的比较挫,各种预处理,一开始还MLEMLE)
感觉这个可以作为莫队算法在一类问题上的优化,因为每一次都是查询[l,r][l,r]这个区间所有不同的质数,最终的答案是
∏i=lra[i]∗∏p−1p\prod_{i=l}^{r}a[i]*\prod{{p-1}\over{p}}
看这个式子,前面一块我们可以维护前缀积,每次查询o(1)o(1)就可以搞定,后面一块要找出区间[l,r][l,r]中所有的不同的质数的函数的乘积,用莫队平均每次查找的复杂度是o(n−−√)o(\sqrt{n}),这个地方能不能优化?
当固定左端点ll的时候,把[l,n][l,n]这段区间所有的质数第一次出现的位置记录下来,把所有出现的质数扔到他们第一次出现的位置上面,用线段树维护的话,每一次查询的复杂度就是$o(logn)。
现在需要解决的是l→l+1l\to{l+1}这个时候线段树维护的区间变成了[l+1,n][l+1,n],即把a[l]a[l]的影响去掉,就是把线段是中ll的位置的乘积变成11(这个地方我写的时候考虑复杂了),那么假如p|a[l]p|a[l]这个时候把pp去掉了,[l+1,n][l+1,n]中仍然有pp的倍数,这个时候要把右边第一个出现pp的倍数的位置的中对应的线段树的位置乘上p−1p{p-1}\over{p},这题就做完了。
复杂度:预处理所有质因数,并且每个数所含有的质数o(M∗logM)o(M*logM),扫一遍[1,n][1,n]中所有能整除的质数的位置o(n∗logM)o(n*logM),树状数组查找o(n∗logn∗logM)o(n*logn*logM)
总的复杂度就是o(M∗logM)+o(n∗logn∗logM)o(M*logM)+o(n*logn*logM)
代码:(自己写的比较挫,各种预处理,一开始还MLEMLE)
#include <bits/stdc++.h> #define LL long long #define FOR(i,x,y) for(int i = x;i < y;++ i) #define IFOR(i,x,y) for(int i = x;i > y;-- i) using namespace std; typedef vector <int> VT; const int Mod = 1e9+7; const int maxn = 1000010; map <int,int> mat; int prime[maxn]; bool check[maxn]; VT pri[maxn]; void get_prime(){ FOR(i,0,maxn) pri[i].clear(); memset(check,false,sizeof(check)); prime[0] = 0; FOR(i,2,maxn){ if(!check[i]){ prime[++prime[0]] = i; pri[i].push_back(i); mat[i] = prime[0]; } FOR(j,1,prime[0]+1){ if(i*prime[j] >= maxn) break; int u = i*prime[j]; check[u] = true; FOR(k,0,(int)pri[i].size()) pri[u].push_back(pri[i][k]); if(i%prime[j] == 0) break; pri[u].push_back(prime[j]); } } } void gcd(LL a,LL b,LL& d,LL& x,LL& y){ if(!b) {d = a; x = 1; y = 0;} else {gcd(b,a%b,d,y,x); y -= x*(a/b);} } LL inv(LL a,LL n){ LL d,x,y; gcd(a,n,d,x,y); return d == 1 ? (x+n)%n : -1; } LL mul[80000],imul[80000]; void init(){ get_prime(); FOR(i,1,prime[0]+1){ mul[i] = (prime[i]-1)*inv(prime[i],Mod)%Mod; imul[i] = prime[i]*inv(prime[i]-1,Mod)%Mod; } } const int maxm = 200020; struct Commends{ int l,r,id; bool operator < (const Commends& rhs) const{ return l < rhs.l; } }cmd[maxm]; const int __cntpri = 80000; queue <int> r_mx[__cntpri]; int n,a[maxm],b[maxm],q; LL c[maxm],res[maxm],sum[maxm],isum[maxm]; int lowbit(int x) {return x&-x;} void modify(int x,LL val){ while(x <= n){ c[x] *= val; c[x] %= Mod; x += lowbit(x); } } LL query(int x){ LL ans = 1; while(x){ ans *= c[x]; ans %= Mod; x -= lowbit(x); } return ans; } void work(){ FOR(i,1,n+1) c[i] = 1; FOR(i,1,prime[0]+1){ if(!r_mx[i].empty()) modify(r_mx[i].front(),mul[i]); } int l = 1; FOR(i,0,q){ res[cmd[i].id] = sum[cmd[i].r]*isum[cmd[i].l-1]%Mod; while(l<cmd[i].l){ FOR(j,0,(int)pri[a[l]].size()){ int u = pri[a[l]][j]; modify(r_mx[mat[u]].front(),imul[mat[u]]); r_mx[mat[u]].pop(); if(!r_mx[mat[u]].empty()){ modify(r_mx[mat[u]].front(),mul[mat[u]]); } } l++; } LL tem = query(cmd[i].r); res[cmd[i].id] = res[cmd[i].id]*tem%Mod; } FOR(i,0,q){ printf("%I64d\n",res[i]); } } int main() { //freopen("test.in","r",stdin); init(); while(~scanf("%d",&n)){ sum[0] = isum[0] = 1; FOR(i,1,n+1) scanf("%d",&a[i]),sum[i] = sum[i-1]*a[i]%Mod,isum[i] = isum[i-1]*inv(a[i],Mod)%Mod; scanf("%d",&q); FOR(i,0,q) scanf("%d%d",&cmd[i].l,&cmd[i].r),cmd[i].id = i; sort(cmd,cmd+q); FOR(i,1,prime[0]+1) while(!r_mx[i].empty()) r_mx[i].pop(); FOR(i,1,n+1){ FOR(j,0,(int)pri[a[i]].size()) r_mx[mat[pri[a[i]][j]]].push(i); } work(); } return 0; }
相关文章推荐
- 关于“一秒一G LiFi比WiFi快一百倍”的评论
- MYSQL数据库的存储过程(Stored Procedure)
- 1064. Complete Binary Search Tree (30)
- 自定义dialog视图
- 地表两个点 的 距离计算公式
- 1064. Complete Binary Search Tree (30)
- React 入门实例教程
- MYSQL数据库的事务(Transaction)
- 安卓游戏开发者:我是如何把包体减了88%
- 232. Implement Queue using Stacks
- dex分包变形记
- 修改PullToRefresh下拉刷新动画
- MYSQL数据库的游标(cursor)
- 一个优质的新闻客户端app
- 如何用Python写一个贪吃蛇AI
- Oracle索引重建
- 【NCRE】——c#判断ppt中的艺术字
- 喜欢用战术上的勤奋去敷衍自己战略上的懒惰——做客服,发货,这两个工作是没有意义的——你懂了怎么发货吗?懂了怎么打包吗?懂了怎么做客服吗?(淘宝的黄金时代已经过去了——做单一产品就是要走量)
- CSS问题:background-image:url(路径问题)
- 20151126测试中问题