树状数组
模板:
const int maxn=10000; //注意:0 是无效下标 int n;//1-n //c[i]所掌控的前缀信息的下标范围是[i−lowbit(i)+1,i],树状数组 c 维护 ai 的前缀和 int c[maxn+1]; //lowbit(x):x的二进制表示里最低位的1与更低位的所有0所组成的二进制数的十进制结果 inline int lowbit(int x){ return x & -x; } //ax+=v void add(int x,int v) { for (int i=x;i<=n;i+=lowbit(i)) c[i]+=v; } //求和a1+a2+...+ax int sum(int x){ int s=0; for(int i=x;i>0;i-=lowbit(i)) s+=c[i]; return s; } //求和al+a(l+1)+...+ar int sum(int l,int r) { return sum(r)-sum(l-1); }
洛谷3374(模板1) 单点修改+区间查询
#include <cstdio> using namespace std; const int maxn=5e5; int n; int c[maxn+3]; inline int lowbit(int x){ return x & -x; } //单点修改 void add(int x,int v) { for (int i=x;i<=n;i+=lowbit(i)) c[i]+=v; } //区间查询 int sum(int x){ int s=0; for(int i=x;i>0;i-=lowbit(i)) s+=c[i]; return s; } int sum(int l,int r) { return sum(r)-sum(l-1); } int main(){ int m,x,a,b; scanf("%d%d",&n,&m); for(int i=0;i<n;++i){ scanf("%d",&x); add(i+1,x); } for(int i=0;i<m;++i){ scanf("%d%d%d",&x,&a,&b); if(x==1){ add(a,b); }else { printf("%d\n",sum(a,b)); } } return 0; }
开始cin cout,TLE,改成scanf后AC。
洛谷3368(模板2)单点查询+区间修改
#include <cstdio> using namespace std; const int maxn=5e5; int n; int c[maxn+3]; inline int lowbit(int x){ return x & -x; } void add(int x,int v) { for (int i=x;i<=n;i+=lowbit(i)) c[i]+=v; } int sum(int x){ int s=0; for(int i=x;i>0;i-=lowbit(i)) s+=c[i]; return s; } void add(int l,int r,int v){ add(l,v); add(r+1,-v); } int main(){ int m,x,a,b,k; scanf("%d%d",&n,&m); int last=0,now; for(int i=0;i<n;++i){ //c里不是差分数组,而是差分数组以树状数组的方式存 scanf("%d",&now); add(i+1,now-last); last=now; } for(int i=0;i<m;++i){ scanf("%d",&x); if(x==1){ //[l,r]区间修改变为在差分数组两端打标记 scanf("%d%d%d",&a,&b,&k); add(a,b,k); }else { //单点查询变为对差分数组求前缀和 scanf("%d",&a); printf("%d\n",sum(a)); } } return 0; }
洛谷3372(模板3)区间修改+区间查询
#include <cstdio> using namespace std; const int maxn=1e5; typedef long long ll; ll n; ll c1[maxn+3],c2[maxn+3];//用树状数组c1维护delti前缀和,用树状数组c2维护i⋅delti前缀和 inline ll lowbit(ll x){ return x & -x; } void add(ll *c,ll x,ll v) { for (int i=x;i<=n;i+=lowbit(i)) c[i]+=v; } ll sum(ll *c,ll x){ ll s=0; for(int i=x;i>0;i-=lowbit(i)) s+=c[i]; return s; } int main(){ ll m,x,a,b,k; scanf("%lld%lld",&n,&m); ll last=0,now; for(int i=0;i<n;++i){ scanf("%lld",&now); add(c1,i+1,now-last); add(c2,i+1,(i+1)*(now-last)); last=now; } for(int i=0;i<m;++i){ scanf("%lld",&x); if(x==1){ scanf("%lld%lld%lld",&a,&b,&k); add(c1,a,k),add(c1,b+1,-k); add(c2,a,k*a),add(c2,b+1,-k*(b+1));//delt数组两端加k,i*delt数组两端就要加i*k }else { scanf("%lld%lld",&a,&b); printf("%lld\n",((b+1)*sum(c1,b)-sum(c2,b))-(a*sum(c1,a-1)-sum(c2,a-1))); } } return 0; }
洛谷1908 逆序对(归并排序or树状数组)下面是树状数组code
#include <cstdio> #include <algorithm> using namespace std; const int maxn=5e5; typedef long long ll; int n; int a[maxn+3],b[maxn+3]; ll c1[maxn+3]; inline ll lowbit(ll x){ return x & -x; } void add(ll *c,ll x,ll v) { for (int i=x;i<=n;i+=lowbit(i)) c[i]+=v; } ll sum(ll *c,ll x){ ll s=0; for(int i=x;i>0;i-=lowbit(i)) s+=c[i]; return s; } int main(){ scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%d",&a[i]); b[i]=a[i]; } sort(b+1,b+1+n); for(int i=1;i<=n;++i){//离散化 a[i]=lower_bound(b+1,b+1+n,a[i])-b; } ll ans=0; for(int i=1;i<=n;++i){//边加入边计算,每次加入a[i]对总逆序对的贡献(只向前看) add(c1,a[i],1); ans+=i-sum(c1,a[i]);//排在a[i]前面,并且大于a[i]的个数 } printf("%lld\n",ans); return 0; }