您的位置:首页 > 其它

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)

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: