树状数组

树状数组

它能干的线段树都可(好像它最基本也就支持单点修改,区间求和——前缀和相减)。。。优点是码量very小

inline void update(int x,int v){
    for(;x<=n;x+=x&(-x))
        c[x]+=v;
}
inline int query(int x){
    int res=0;
    for(;x>0;x-=x&(-x))
        res+=c[x];
    return res;
}

区间修改单点查询就把原数组差分一下就可详见

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
inline LL read(){
	LL x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=1000000;
LL n,m;
LL a,c[N];
inline void update(LL x,LL v){
    for(;x<=N;x+=x&(-x))
        c[x]+=v;
}
inline LL query(LL x){
    LL res=0;
    for(;x;x-=x&(-x))
        res+=c[x];
    return res;
}
int main(){
    n=read();m=read();
    LL i,now=0;
    for(i=1;i<=n;i++){
        a=read();
        update(i,a-now);
        now=a;
    }
    LL op;LL x,y,k;
    for(i=1;i<=m;i++){
    	op=read();
        if(op==1){
            x=read();y=read();k=read();
            update(x,k);
            update(y+1,-k);
        }else{
            x=read();printf("%lld\n",query(x));
        }
    }
    return 0;
}

树状数组求逆序对

非常神的题

本质和这个一样

离散化一下

按价值从小到大排序

排完序之后用权值树状数组维护

比当前的数大的有$query(n)-query(a[i]) $个,先统计答案,然后再把这个数插进去

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=1e6+10;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,a[N],b[N];
int c[N];
void upd(int x,int v){for(int i=x;i<=n;i+=i&(-i)) c[i]+=v;}
int query(int x){int res=0;for(int i=x;i;i-=i&(-i)) res+=c[i];return res;}
int main() {
	n=read();
	for(int i=1;i<=n;i++) b[i]=a[i]=read();
	sort(b+1,b+1+n);
	int len=unique(b+1,b+1+n)-b-1;
	for(int i=1;i<=n;i++) 
		a[i]=lower_bound(b+1,b+1+len,a[i])-b;
	ll ans=0;
	for(int i=1;i<=n;i++) {
		ans+=query(n)-query(a[i]);
		upd(a[i],1);
	}
	printf("%lld\n",ans);
	return 0;
}

老C的任务

­把询问拆分,­按\(x\)轴排序,\(y\)轴维护树状数组 ­,­把区间询问转化为前缀和相减

注意!!!树状数组要往上加到非常大大大

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
inline ll read(){
	ll x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=2000005;
int n,m,len,tot,cnt;
struct point{
    int x,y,p;
}a[N];
struct ques{
    int x1,y1,x2,y2;
}q[N];
struct line{
    int l,r,h,id;
}li[N];

ll c[N];
inline void upd(int x,ll v){for(;x<=4*N;x+=x&(-x))c[x]+=v;}
inline ll query(int x){ll res=0;for(;x>0;x-=x&(-x))res+=c[x];return res;}

ll b[N];
bool cmp1(point a,point b){return a.x<b.x;}
bool cmp2(line a,line b){return a.h<b.h;}

ll ans[N];
int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++){
        a[i].x=read();a[i].y=read();a[i].p=read();
        b[++len]=a[i].x;b[++len]=a[i].y;
    }
    for(int i=1;i<=m;i++){
        q[i].x1=read();q[i].y1=read();q[i].x2=read();q[i].y2=read();
        b[++len]=q[i].x1-1;b[++len]=q[i].y1-1;b[++len]=q[i].x2;b[++len]=q[i].y2;
    }
    sort(b+1,b+len+1);
    tot=unique(b+1,b+1+len)-b-1;
    for(int i=1;i<=n;i++){
        a[i].x=lower_bound(b+1,b+1+len,a[i].x)-b;
        a[i].y=lower_bound(b+1,b+1+len,a[i].y)-b;
    }
    for(int i=1;i<=m;i++){
        q[i].x1=lower_bound(b+1,b+1+len,q[i].x1-1)-b;
        q[i].y1=lower_bound(b+1,b+1+len,q[i].y1-1)-b;
        q[i].x2=lower_bound(b+1,b+1+len,q[i].x2)-b;
        q[i].y2=lower_bound(b+1,b+1+len,q[i].y2)-b;
        li[i*2-1].h=q[i].x1;li[i*2-1].l=q[i].y1;li[i*2-1].r=q[i].y2;li[i*2-1].id=i;
        li[i*2].h=q[i].x2;li[i*2].l=q[i].y1;li[i*2].r=q[i].y2;li[i*2].id=i;
    }

    sort(a+1,a+1+n,cmp1);
    sort(li+1,li+1+2*m,cmp2);
    int now=1;
    for(int i=1;i<=2*m;i++){
        while(a[now].x<=li[i].h&&now<=n){
            upd(a[now].y,a[now].p); now++;
        }
        if(!ans[li[i].id]) ans[li[i].id]=query(li[i].r)-query(li[i].l);
        else ans[li[i].id]=query(li[i].r)-query(li[i].l)-ans[li[i].id];
    }
    for(int i=1;i<=m;i++)
        printf("%lld\n",ans[i]);

    return 0;
}

Count 二维树状数组

就是多了层for

然后数颜色加一维状态 \([c]\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
inline ll read(){
	ll x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=305;
int n,m,q;
int a[N][N];
int t[105][N][N];
void upd(int x,int y,int c,int v) {
	for(int i=x;i<=n;i+=i&(-i))
		for(int j=y;j<=m;j+=j&(-j))
		 	t[c][i][j]+=v;
}
int query(int x,int y,int c) {
	int res=0;
	for(int i=x;i;i-=i&(-i))
		for(int j=y;j;j-=j&(-j))
		 	res+=t[c][i][j];
	return res;	
}
int main(){
    n=read();m=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++) {
			a[i][j]=read();
			upd(i,j,a[i][j],1);
		}
    q=read();
    int op,x1,x2,y1,y2,c;
    for(int i=1;i<=q;i++){
        op=read();
        if(op==1){
            x1=read();y1=read();c=read();
            upd(x1,y1,a[x1][y1],-1);
            a[x1][y1]=c;
            upd(x1,y1,a[x1][y1],1);
        }
		else{
            x1=read();x2=read();y1=read();y2=read();c=read();
            printf("%d\n",query(x2,y2,c)+query(x1-1,y1-1,c)-query(x1-1,y2,c)-query(x2,y1-1,c)); 
        }
    }	
    return 0;
}

梦原

如果新的点比并上去的点苹果少,那么每次往上并上一个新的点,所产生的代价是0

如果新的点比并上去的点苹果多,那你就需要多花 多出来的苹果数 那么多,
那么对于新加的点\(i\),所产生的期望代价就是看\([i - k,i - 1]\)这里面的点哪些点比新点的\(val\)少,答案加上差值,然后除\(k\)

这可以用离散化的权值树状数组维护

具体代码中的\(a[i]\)是离散后的排名,\(c[a[i]]\)就是原始值

我们要维护区间 \([i-k,i-1]\)\(sum_a[i]\)及 其个数\(siz\),产生的代价即为\(a[now]*siz-sum_a[i]\);

#include <queue>
#include <cstdio>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>

using namespace std;
const int N=2005000;
const int P=998244353;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
#define MP make_pair
typedef long long LL;
int n,m,k;
int a[N],c[N]; 
LL inv[N];
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}
inline void plu(int &x,int y){x+=y;x>=P&&(x-=P);}
int ct[N],cd[N];//权值树状数组——个数/和
inline void upd(int x,int v,int vv) {
	for(;x<=m;x+=x&(-x)) plu(cd[x],v),ct[x]+=vv;
}
inline pair<int,int> query(int x) {
	int sum=0,siz=0;
	for(;x;x-=x&(-x)) plu(sum,cd[x]),siz+=ct[x];
	return MP(sum,siz);
}

int main() {
	n=read();k=read();
	for(int i=1;i<=n;i++) c[i]=a[i]=read();
	sort(c+1,c+1+n);m=unique(c+1,c+1+n)-c-1;
	LL ans=a[1];
	a[1]=lower_bound(c+1,c+1+m,a[1])-c;
	upd(a[1],c[a[1]],1);
	
	inv[0]=inv[1]=1;
	for(int i=2;i<=k;i++)
		inv[i]=(P-P/i)*inv[P%i]%P;
	for(int i=2;i<=n;i++) {
		if(i-k-1>=1) upd(a[i-k-1],P-c[a[i-k-1]],-1);
		a[i]=lower_bound(c+1,c+1+m,a[i])-c;
		pair<int,int> s=query(a[i]);
		ans=(ans+inv[min(k,i-1)]*(1ll*c[a[i]]*s.second%P-s.first+P)%P)%P;
		upd(a[i],c[a[i]],1);
	}
	printf("%lld\n",ans);
	return 0;
}


posted @ 2020-08-14 00:26  ke_xin  阅读(34)  评论(0编辑  收藏  举报