树状数组区间更新区间查询以及gcd的logn性质

题目描述

给你一个长为n的序列a

m次查询

每次查询一个区间的所有子区间的gcd的和mod1e9+7的结果

输入描述:

第一行两个数n,m之后一行n个数表示a之后m行每行两个数l,r表示查询的区间

输出描述:

对于每个询问,输出一行一个数表示答案

示例1

输入

5 7
30 60 20 20 20
1 1
1 5
2 4
3 4
3 5
2 5
2 3

输出

30
330
160
60
120
240
100

说明

[1,1]的子区间只有[1,1],其gcd为30[1,5]的子区间有:[1,1]=30,[1,2]=30,[1,3]=10,[1,4]=10,[1,5]=10[2,2]=60,[2,3]=20,[2,4]=20,[2,5]=20[3,3]=20,[3,4]=20,[3,5]=20[4,4]=20,[4,5]=20[5,5]=20总共330[2,4]的子区间有:[2,2]=60,[2,3]=20,[2,4]=20[3,3]=20,[3,4]=20[4,4]=20总共160[3,4]的子区间有:[3,3]=20,[3,4]=20[4,4]=20总共60[3,5]的子区间有:[3,3]=20,[3,4]=20,[3,5]=20[4,4]=20,[4,5]=20[5,5]=20总共120[2,5]的子区间有:[2,2]=60,[2,3]=20,[2,4]=20,[2,5]=20[3,3]=20,[3,4]=20,[3,5]=20[4,4]=20,[4,5]=20[5,5]=20总共240[2,3]的子区间有:[2,2]=60,[2,3]=20[3,3]=20总共100

http://blog.csdn.net/fsahfgsadhsakndas/article/details/52650026  //详解

我们假设sigma(r,i)表示r数组的前i项和,调用一次的复杂度是log2(i)

设原数组是a[n],差分数组c[n],c[i]=a[i]-a[i-1],那么明显地a[i]=sigma(c,i),如果想要修改a[i]到a[j](比如+v),只需令c[i]+=v,c[j+1]-=v

【今天的主要内容】

我们可以实现NlogN时间的“单点修改,区间查询”,“区间修改,单点查询”,其实后者就是前者的一个变形,要明白树状数组的本质就是“单点修改,区间查询”

怎么实现“区间修改,区间查询”呢?

观察式子:
a[1]+a[2]+...+a[n]

= (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n])

= n*c[1] + (n-1)*c[2] +... +c[n]

= n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n])    (式子①)

那么我们就维护一个数组c2[n],其中c2[i] = (i-1)*c[i]

每当修改c的时候,就同步修改一下c2,这样复杂度就不会改变

那么

式子①

=n*sigma(c,n) - sigma(c2,n)



#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+88;
const int mod=1e9+7;
int n,m,a[N],v[250],pos[250];
long long f[N],g[N],ans[N];
struct node{
    int l,r,id;
    bool operator < (const node &A)const{
    return r<A.r;
    }
}q[N];
void add(int x,int y){
    for(int i=x;i<=n;i+=i&(-i)) f[i]+=y,g[i]+=1LL*(x-1)*y;
}
long long query(int u){
    long long ans=0;
    for(int i=u;i;i-=i&(-i)) ans=(ans+1LL*u*f[i]-g[i])%mod;
    return ans;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d",a+i);
    for(int i=1;i<=m;++i) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    sort(q+1,q+m+1);
    int tot=0,now;
    for(int i=1,p=1;i<=m;++i) {
        while(p<=q[i].r) {
            for(int j=1;j<=tot;++j) v[j]=__gcd(v[j],a[p]);
            v[++tot]=a[p],pos[tot]=p;
            now=tot,tot=0;
            for(int j=1;j<=now;++j) if(v[j]!=v[j-1]) v[++tot]=v[j],pos[tot]=pos[j];
            for(int j=1;j<tot;++j) add(pos[j],v[j]),add(pos[j+1],-v[j]);
            add(pos[tot],v[tot]),add(p+1,-v[tot]);
            ++p;
        }
        ans[q[i].id]=query(q[i].r)-query(q[i].l-1);
    }
    for(int i=1;i<=m;++i) printf("%lld\n",(ans[i]+mod)%mod);
} 

原文地址:https://www.cnblogs.com/mfys/p/8228132.html

时间: 2024-10-05 05:02:17

树状数组区间更新区间查询以及gcd的logn性质的相关文章

POJ 1195-Mobile phones(二维树状数组-区间更新区间查询)

Mobile phones Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 17661   Accepted: 8173 Description Suppose that the fourth generation mobile phone base stations in the Tampere area operate as follows. The area is divided into squares. The

树状数组区间更新区间查询

1 void ins(int k,int x,int t){ 2 for (; x<=tot; x+=x&-x) c[k][x]+=t; 3 } 4 ll getsum(int k,int x){ 5 ll t=0; for (; x; x-=x&-x) t+=c[k][x]; return t; 6 } 7 void mdy(int x,int y,int z){ 8 ins(0,x,z); ins(1,x,z*(x-1)); ins(0,y+1,-z); ins(1,y+1,-z

树状数组区间更新

在今天的文章开始之前,给大家提一个建议,由于线段树和树状数组这两个结构的分析有很多联系,因此,建议没有看前几篇文章的朋友一定需要了解一下前面的内容.链接如下: 线段树+RMQ问题第二弹 线段树第二弹(区间更新) 树状数组(Binary Indexed Tree,BIT) 上篇文章我们讨论了树状数组的基本结构以及它最擅长的两个功能:单点更新和区间求和,今天,我们来接着上一篇文章的内容继续深入研究.上篇文章我们是将树状数组和线段树进行对比讲解的,既然线段树的章节我们介绍了区间求和.区间最值.单点更新

【bzoj3779】重组病毒 LCT+树上倍增+DFS序+树状数组区间修改区间查询

题目描述 给出一棵n个节点的树,每一个节点开始有一个互不相同的颜色,初始根节点为1. 定义一次感染为:将指定的一个节点到根的链上的所有节点染成一种新的颜色,代价为这条链上不同颜色的数目. 现有m次操作,每次为一下三种之一: RELEASE x:对x执行一次感染: RECENTER x:把根节点改为x,并对原来的根节点执行一次感染: REQUEST x:询问x子树中所有节点感染代价的平均值. 输入 输入的第一行包含两个整数n和m,分别代表局域网中计算机的数量,以及操作和询问的总数.接下来n-1行,

HRBUST 1161 树状数组区间更新求和

Leyni Time Limit: 3000 MS Memory Limit: 65536 K Total Submit: 267(64 users) Total Accepted: 82(57 users) Rating: Special Judge: No Description Leyni被人掳走,身在水深火热之中... 小奈叶为了拯救Leyni,独自一人前往森林深处从静竹手中夺回昏迷中的Leyni. 历经千辛万苦,小奈叶救出了Leyni,但是静竹为此极为恼怒,决定对他们发起最强烈的进攻.

hdu 1556:Color the ball(第二类树状数组 —— 区间更新,点求和)

Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8984    Accepted Submission(s): 4594 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球

树状数组 区间修改+区间查询

其实直到不久前都几乎不会用树状数组,请教了PPZ大佬之后终于懂了一点点. 然后小蒟蒻现在才知道了树状数组区间修改+区间查询的方法QAQ 传送门 Codevs 线段树练习3 //Twenty #include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<queu

【bzoj5173】[Jsoi2014]矩形并 扫描线+二维树状数组区间修改区间查询

题目描述 JYY有N个平面坐标系中的矩形.每一个矩形的底边都平行于X轴,侧边平行于Y轴.第i个矩形的左下角坐标为(Xi,Yi),底边长为Ai,侧边长为Bi.现在JYY打算从这N个矩形中,随机选出两个不同的矩形,并计算它们的并的大小.JYY想知道,交的大小的期望是多少.换句话说即求在所有可能的选择中,两个矩形交的面积的平均大小是多大. 输入 输入一行包含一个正整数N. 接下来N行,每行4个整数,分别为Xi,Yi,Ai,Bi 2 < =  N < =  2*10^5, 0 < =  Xi,

hdu 4970 树状数组区间更新 思维题

http://acm.hdu.edu.cn/showproblem.php?pid=4970 好像还没有用树状数组写过区间更新,但是树状数组的确比线段树快很多,不知道跟ZKW线段树比效率怎么样: 先贴个模板: #include <cstdio> const int MAXN = 1024; int B[MAXN], C[MAXN]; #define LOWBIT(x) ((x)&(-(x))) void bit_update(int *a, int p, int d) { for (