問題描述
小蔥喜歡除法,所以他給了你N個(gè)數(shù)a1, a2, ?, aN,并且希望你執(zhí)行M次操作,每次操作可能有以下兩種:
給你三個(gè)數(shù)l, r, v,你需要將al, al+1, ?, ar之間所有v的倍數(shù)除以v。
給你兩個(gè)數(shù)l, r,你需要回答al + al+1 + ? + ar的值是多少。
輸入格式
第一行兩個(gè)整數(shù)N, M,代表數(shù)的個(gè)數(shù)和操作的次數(shù)。
接下來(lái)一行N個(gè)整數(shù),代表N個(gè)數(shù)一開始的值。
輸出格式
對(duì)于每一次的第二種操作,輸出一行代表這次操作所詢問的值。
樣例輸入
5 3
1 2 3 4 5
2 1 5
1 1 3 2
2 1 5
1
2
3
4
5
樣例輸出
15
14
1
2
評(píng)測(cè)用例規(guī)模與約定
對(duì)于30%的評(píng)測(cè)用例,1 ≤ N, M ≤ 1000;
對(duì)于另外20%的評(píng)測(cè)用例,第一種操作中一定有l(wèi) = r;
對(duì)于另外20%的評(píng)測(cè)用例,第一種操作中一定有l(wèi) = 1 , r = N;
對(duì)于100%的評(píng)測(cè)用例,1 ≤ N, M ≤ 105,0 ≤ a1, a2, ?, aN ≤ 106, 1 ≤ v ≤ 106, 1 ≤ l ≤ r ≤ N。
一開始看這個(gè)題,就是線段樹嘛,我還想最后一個(gè)題目怎么這么簡(jiǎn)單.就寫了線段樹,提交了一發(fā),超時(shí)了.好吧1e6的數(shù)據(jù),不超時(shí)才怪…才想起來(lái)的樹狀數(shù)組,今天才發(fā)現(xiàn)她的魅力.時(shí)間復(fù)雜度O(logn),空間復(fù)雜度O(n).怎么會(huì)有這么好的東西…比線段樹足足提高了一個(gè)檔次.兩個(gè)代碼都粘一下吧,畢竟線段樹寫了很久,第一次正兒八經(jīng)的自己寫線段樹.
超時(shí)線段樹:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;const int maxx=1e5+10;
int ary[maxx];
struct node{int l;int r;int sum;
}p[maxx*4];
int n,m;void pushup(int cur)
{p[cur].sum=p[2*cur].sum+p[2*cur+1].sum;
}void build(int l,int r,int cur)
{p[cur].l=l;p[cur].r=r;p[cur].sum=0;if(l==r){p[cur].sum=ary[l];return ; }int mid=(l+r)/2;build(l,mid,2*cur);build(mid+1,r,2*cur+1);pushup(cur);
}int query(int l,int r,int cur)
{if(l<=p[cur].l&&r>=p[cur].r){return p[cur].sum;}int mid=(p[cur].l+p[cur].r)/2;if(mid>=r) return query(l,r,2*cur);else if(mid<l) return query(l,r,2*cur+1);else return query(l,mid,2*cur)+query(mid+1,r,2*cur+1);
}void update(int L,int R,int v,int cur)
{int l=p[cur].l;int r=p[cur].r;if(l==r&&l<=l&&l<=R){//cout<<p[cur].sum<<endl;if(p[cur].sum%v==0) p[cur].sum/=v;return ;}int mid=(l+r)/2;if(mid>=R) update(L,R,v,2*cur);else if(mid<L) update(L,R,v,2*cur+1);else{update(L,mid,v,2*cur);update(mid+1,R,v,2*cur+1);}pushup(cur);
}int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&ary[i]);build(1,n,1);int t,l,r,v;while(m--){scanf("%d",&t);if(t==1){scanf("%d%d%d",&l,&r,&v);update(l,r,v,1);}else if(t==2){scanf("%d%d",&l,&r);cout<<query(l,r,1)<<endl;}}
}
樹狀數(shù)組:
樹狀數(shù)組就是跳躍著去找有關(guān)聯(lián)的一些點(diǎn),而不像線段樹那樣去二分找點(diǎn),這樣就大大的節(jié)省了時(shí)間.而且在空間上不需要開結(jié)構(gòu)體,也節(jié)省空間.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;const int maxx=1e5+10;
ll ary[maxx];
ll tree[maxx];
int n,m;int lowbit(int i)//樹狀數(shù)組的精華
{return i&-i;
}ll getsum(int i)
{ll sum=0;while(i>0){sum+=tree[i];i-=lowbit(i);}return sum;
}void update(int i,int x)
{while(i<=n){tree[i]+=x;i+=lowbit(i);}
}int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&ary[i]);update(i,ary[i]);}int t,l,r,v;while(m--){scanf("%d",&t);if(t==1){scanf("%d%d%d",&l,&r,&v);while(l<=r){if(ary[l]%v==0){update(l,-(ary[l]-ary[l]/v));ary[l]=ary[l]/v;}++l;}}else if(t==2){scanf("%d%d",&l,&r);printf("%lld\n",getsum(r)-getsum(l-1));}}
}
努力加油a啊,(o)/~
總結(jié)
以上是生活随笔為你收集整理的201709-5 除法 ccf(树状数组)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。