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.
- 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).
- 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;
}