树状数组合集

Matrix
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 21746 Accepted: 8135
Description

Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the i-th row and j-th column. Initially we have A[i, j] = 0 (1 <= i, j <= N).

We can change the matrix in the following way. Given a rectangle whose upper-left corner is (x1, y1) and lower-right corner is (x2, y2), we change all the elements in the rectangle by using “not” operation (if it is a ‘0’ then change it into ‘1’ otherwise change it into ‘0’). To maintain the information of the matrix, you are asked to write a program to receive and execute two kinds of instructions.

  1. C x1 y1 x2 y2 (1 <= x1 <= x2 <= n, 1 <= y1 <= y2 <= n) changes the matrix by using the rectangle whose upper-left corner is (x1, y1) and lower-right corner is (x2, y2).
  2. Q x y (1 <= x, y <= n) querys A[x, y].
    Input

The first line of the input is an integer X (X <= 10) representing the number of test cases. The following X blocks each represents a test case.

The first line of each block contains two numbers N and T (2 <= N <= 1000, 1 <= T <= 50000) representing the size of the matrix and the number of the instructions. The following T lines each represents an instruction having the format “Q x y” or “C x1 y1 x2 y2”, which has been described above.
Output

For each querying output one line, which has an integer representing A[x, y].

There is a blank line between every two continuous test cases.
Sample Input

1
2 10
C 2 1 2 2
Q 2 2
C 2 1 2 1
Q 1 1
C 1 1 2 1
C 1 2 1 2
C 1 1 2 2
Q 1 1
C 1 1 2 1
Q 2 1
Sample Output

1
0
0
1
translate:
给一个N*N的矩阵A,其中元素是0或1。A[i][j]表示在第i行第j列的数。最初时,A[i][j]=0(1<=i,j<=N)。我们以以下方式来改变矩阵,给定一个矩形的左上角为(x1,y1)和右下角为(x2,y2),我们对这个矩形范围内的所有元素进行“非”操作(如果它是一个’0’,那么变化为’1’,否则它变为’0’)。请你编写一个程序完成以下两种操作:
1. C x1 y1 x2 y2 (1<=x1<=x2<=n,1<=y1<=y2<=n) 改变左上角为(x1,y1)和右下角为(x2,y2)矩形范围内的值。
2. Q x y (1 <= x, y <= n) 询问A[x][y]的值。


树状数组解决的很好嘛

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define INF 1000000000
#define cyc(i,n) for(i=1;i<=n;i++)
#define read(x) scanf("%d",&x)
using namespace std;
int a[1030][1030],n,m;

void Add(int x,int y,int d)
{
//  if(!x||!y) return ;
    for(int i=x;i<=n;i+=i&(-i)) 
    for(int j=y;j<=n;j+=j&(-j))
    a[i][j]+=d;
}
int Sum(int x,int y)
{
    int ans=0;
    for(int i=x;i>0;i-=i&(-i)) 
    for(int j=y;j>0;j-=j&(-j))
    ans+=a[i][j]; 
    return ans;
}
int main()
{
     int T,i,x,y,xx,yy;
     read(T);
     while(T--)
     {
        memset(a,0,sizeof(a));
        char cc;
        read(n);read(m);
        cyc(i,m)
        {
            cin>>cc;
            if(cc=='Q')
            {
                read(x);read(y);
                printf("%d\n",Sum(x,y)%2);
            }else
            {
                read(xx);read(yy);read(x);read(y);
                Add(x+1,y+1,1);Add(x+1,yy,1);Add(xx,y+1,1);Add(xx,yy,1);
            }
        }
        printf("\n");
     }
     return 0;
}

POJ2352】Stars 夜空星辰
Description
夜空中有N颗恒星(N≤100000),每颗恒星具有其坐标(x, y)(0≤x, y≤100000)。现在,天文学家要对这些恒星进行分类,分类的标准如下:对于任意一颗恒星S(x,y),如果存在k颗恒星,其x, y坐标均不大于S,则恒星S属于k类星。
如下图所示:第5颗恒星为3类星,这是由1、2、4三颗恒星均在其左下方而得出的,类似地第2、4两颗恒星为1类星,第3颗恒星为2类星。因此在这幅图中只有一颗0类星,共有二颗1类星,2类星和3类星各有一颗。
现给出N颗恒星的坐标,要求统计出0~N-1类星的个数。

Input
输入文件第一行包含一个整数N,表示恒星总数。
接下来的N行每行两个整数表示一颗恒星的坐标。不存在两颗星拥有相同的坐标。
Output
输出文件包含N行,每行包含一个整数,第i行表示第i-1类星的数量。
Sample Input
5
3 3
5 1
5 5
1 1
7 1
Sample Output
1
2
1
1
0
Hint
【数据范围】
  对于20%的数据,n<=1000;
  对于100%的数据, n<=100000;


#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define INF 1000000000
#define cyc(i,n) for(i=1;i<=n;i++)
#define read(x) scanf("%d",&x)
using namespace std;
int a[100010],n,m,l[100010];
struct Node{
    int x,y;
}b[100010];
void Add(int x,int y)
{
    for(int i=x;i<=n;i+=i&(-i)) a[i]+=y;
}
int Sum(int x)
{
    int i,ans=0;
    for(i=x;i>0;i-=i&(-i)) ans+=a[i]; 
    return ans;
}

int cmp(Node p,Node q)
{
    return (p.x==q.x) ? p.y<q.y : p.x<q.x ;
}
int main()
{
     int i;
     read(n);
     cyc(i,n)
     {
        read(b[i].x);read(b[i].y);
     }
     sort(b+1,b+n+1,cmp);
     cyc(i,n)
     {
        l[Sum(b[i].y+1)]++;
        Add(b[i].y+1,1); 
     }
     for(i=0;i<n;i++) printf("%d\n",l[i]);
     return 0;
}

【POJ3468】区间操作
Description
给你N个整数A[1], A[2], … , A[N]。你需要处理两类问题:
“C a b c”表示给A[a], A[a+1], … , A[b]之间的每个数都加上c(-10000≤c≤10000)。
“Q a b”求A[a], A[a+1], … , A[b]之间数字的总和;
Input
输入的第一行包含两个整数N和Q(1≤N,Q≤100000);
第二行包含N个整数Ai(-10^9≤Ai≤10^9)
接下来Q行,表示Q个问题,形式如题;
Output
输出要求计算出的区间总和,每行一个。
Sample Input
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
Sample Output
4
55
9
15


线段树。。。

#include<stdio.h>

struct node{int l,r; long long sum;};
const int N=100003;
node tree[10*N];
int i,n,q,l,r,d,a[N];
char c;
long long tag[N*10]={0};
void build(int l,int r,int s)//build(1,n,1);
{
    tree[s].l=l; tree[s].r=r;
    if (l==r) {tree[s].sum=a[l]; return;}
    int m=l+r>>1;
    build(l,m,s<<1);
    build(m+1,r,s<<1|1);//s<<1|1------s*2+1
    tree[s].sum=tree[s<<1].sum+tree[s<<1|1].sum;
}

void pushdown(int s,int m)
{
    tag[s<<1]+=tag[s];
    tag[s<<1|1]+=tag[s];
    tree[s<<1].sum+=tag[s]*(m-(m>>1));
    tree[s<<1|1].sum+=tag[s]*(m>>1);
    tag[s]=0;
}

void add(int l,int r,int s)
{
    if (l<=tree[s].l&&tree[s].r<=r) {tag[s]+=d; tree[s].sum+=d*(tree[s].r-tree[s].l+1); return;}
    if (tag[s]) pushdown(s,tree[s].r-tree[s].l+1);
    int m=tree[s].l+tree[s].r>>1;
    if (l<=m) add(l,r,s<<1);
    if (r>m) add(l,r,s<<1|1);
    tree[s].sum=tree[s<<1].sum+tree[s<<1|1].sum;
}

long long ask(int l,int r,int s)
{
    if (l<=tree[s].l&&tree[s].r<=r) return tree[s].sum;
    if (tag[s]) pushdown(s,tree[s].r-tree[s].l+1);
    int m=tree[s].l+tree[s].r>>1;
    long long tmp=0;
    if (l<=m) tmp+=ask(l,r,s<<1);
    if (r>m) tmp+=ask(l,r,s<<1|1);
    return tmp;
}

int main()
{
    //freopen("poj3468.in","r",stdin);
    //freopen("poj3468.out","w",stdout);
    scanf("%d%d\n",&n,&q);
    for (i=1;i<=n;i++) scanf("%d",&a[i]); scanf("\n");
    build(1,n,1);
    for (i=1;i<=q;i++){
        scanf("%c",&c);
        if (c=='C'){
            scanf("%d%d%d\n",&l,&r,&d);
            add(l,r,1);
        }
        else{
            scanf("%d%d\n",&l,&r);
            printf("%lld\n",ask(l,r,1));
        }
    }
    return 0;
}

其实不用线段树,还是树状数组

#include<cstdio>
using namespace std;

const int NUM=100005;
long long n,m,a[NUM],c1[NUM],c2[NUM];

long long GetLL()
{   long long ret=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
    return ret*f;
}
long long lowbit(long long x){return x&(-x);}
void Add(long long x,long long k)
{   long long i;
    for(i=x;i<=n;i+=lowbit(i)){c1[i]+=k;c2[i]+=x*k;}
}
long long Sum(long long x)
{   long long Ans1=0,Ans2=0,i;
    for(i=x;i>0;i-=lowbit(i)){Ans1+=c1[i];Ans2+=c2[i];}
    return Ans1*(x+1)-Ans2;
}
int main(){
    long long i,x,y;char ch;a[0]=0;
    n=GetLL();m=GetLL();
    for(i=1;i<=n;i++){a[i]=GetLL();a[i]+=a[i-1];}
    while(m--)
    { while(ch=getchar())if(ch=='Q'||ch=='C')break;
      x=GetLL();y=GetLL();
      if(ch=='C'){i=GetLL();Add(x,i);Add(y+1,-i);}
      else printf("%lld\n",a[y]-a[x-1]+Sum(y)-Sum(x-1));
    }
    return 0;
}

【IOI 2001】移动电话
Description
   假设第四代移动电话的收发站是这样运行。整个区域被分割成很小的方格。所有的方格组成了一个S*S的矩阵,行和列从0~S-1编号。每个小方格都包含一个收发站。每个方格内的开机的移动电话数量可以不断改变,因为手机用户在各个方格之间移动,也有用户开机或者关机。一旦某个方格里面开机的移动电话数量发生了变化,该方格里的收发站就会向总部发送一条信息说明这个改变量。
   总部要你写一个程序,用来管理从各个收发站收到的信息。老板可能随时会问:某一个给定矩形区域内有多少部开机的移动电话啊?你的程序必须要能随时回答老板的问题。
输入输出数据要求
   从标准输入读入整数,向标准输出写入你对老板问题的回答。
   输入数据的格式如下:每个输入独立成一行。一个输入包括一个指示数和一些参数,见下表:
   
   这里写图片描述
  所有的数据总是在给定的范围内,你不需要查错。特别的,如果A是负数,你可以认为该操作不会让该格子的开机移动电话数变成负数。格子是从0开始编号的,比如一个4*4的区域,所有的格子(X,Y)应该表示为0<=X<=3,0<=Y<=3。
Input
 0 4 //初始化44的区域.
 1 1 2 3 //格子(1,2)加3。
 2 0 0 2 2 //询问矩形0<=X<=2,0<=Y<=2里面的开机移动电话总量
 1 1 2 -1 //格子(1,2)减1。
 2 1 1 2 3 //询问矩形1<=X<=2,1<=Y<=3里面的开机移动电话总量。
 3
Output
   3 //回答询问
   2 //回答询问


#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define INF 1000000000
#define cyc(i,n) for(i=1;i<=n;i++)
#define read(x) scanf("%d",&x)
using namespace std;
int a[1030][1030],n,m;

void Add(int x,int y,int d)
{
    for(int i=x;i<=n;i+=i&(-i)) 
    for(int j=y;j<=n;j+=j&(-j))
    a[i][j]+=d;
}
int Sum(int x,int y)
{
    int ans=0;
    for(int i=x;i>0;i-=i&(-i)) 
    for(int j=y;j>0;j-=j&(-j))
    ans+=a[i][j]; 
    return ans;
}
int main()
{
     int i,x,y,r,t,num,d;
     read(x);read(n);
     while(1)
     {
        read(num);
        if(num==1)
        {
            read(x);read(y);read(d);
            Add(x+1,y+1,d);
        }else
        if(num==2)
        {
            read(x);read(y);read(r);read(t);
            printf("%d\n",Sum(r+1,t+1)-Sum(r+1,y)-Sum(x,t+1)+Sum(x,y));
        }else break;
     }
     return 0;
}

还能解决逆序对问题哦

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define INF 1000000000
#define cyc(i,n) for(i=1;i<=n;i++)
#define read(x) scanf("%d",&x)
using namespace std;
int a[100010],n,m,c[101010],ans;
struct node{
    int x,y;
}b[100010]; 
void Add(int x,int y)
{
    for(int i=x;i<=n;i+=i&(-i)) a[i]+=y;
}
int Sum(int x)
{
    int i,ans=0;
    for(i=x;i>0;i-=i&(-i)) ans+=a[i]; 
    return ans;
}
int cmp(node p, node q)
{
    return p.x <q.x ;
}
int main()
{
     int i;
     read(n);
     cyc(i,n)
     {
        read(b[i].x);b[i].y=i;
     }
     sort(b+1,b+n+1,cmp);
     cyc(i,n)
     {
        c[b[i].y]=i;
     }
     cyc(i,n)
     {
        ans+=Sum(n)-Sum(c[i]);
        Add(c[i],1);
     }
     cout<<ans;
     return 0;
}

再粘一个逆序对题
弱点
weakness.pas/c/cpp
【题目描述】
一队勇士正在向你进攻,每名勇士都有一个战斗值ai。但是这队勇士却有一个致命弱点,如果存在i<=j<=k使得ai>aj>ak,则会影响他们整体的战斗力。我们将这样的一组(i,j,k)称为这队勇士的一个弱点。请求出这队勇士的弱点数目。
【输入】
输入文件:weakness.in
输入的第一行是一个整数n,表示勇士的数目。
接下来一行包括n个整数,表示每个勇士的战斗值ai。
【输出】
输入文件:weakness.out
输出为一行,包含一个整数。表示这队勇士的弱点数目。
【输入样例】
4
10 8 3 1
【输出样例】
4
【数据范围】
对于30%的数据,3<=n<=100
对于100%的数据,3<=n<=1000000
对于100%的数据,1<=ai<=1000000,每个ai均不相同


枚举每个点
左面比它大的以及右面比它小的乘起来再加起来
实现看代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
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 x*f;
}
ll ans;
int n;
int t[2][1000005],a[1000005];
void add(int f,int x,int val)
{
    for(int i=x;i<=1000000;i+=i&(-i))
        t[f][i]+=val;
}
ll query(int f,int x)
{
    ll sum=0;
    for(int i=x;i;i-=i&(-i))
        sum+=t[f][i];
    return sum;
}
int main()
{
    freopen("weakness.in","r",stdin);
    freopen("weakness.out","w",stdout); 
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    for(int i=1;i<=n;i++)
        add(0,a[i],1);
    for(int i=1;i<=n;i++)
    {
        add(0,a[i],-1);
        ans+=(query(1,1000000)-query(1,a[i]))*query(0,a[i]-1);
        add(1,a[i],1);
    }
    printf("%I64d",ans);
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值