HDU 1166(敌兵布阵)
题目大意:(1) Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30)
(2)Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30);
(3)Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数;
(4)End 表示结束,这条命令在每组数据最后出现;
题目分析:目的是纪念我线段树的开端,也是为了今后的回顾。其实就是基本操作,更新跟建树。
源程序+部分注释:
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; struct node{ int sum,a,b; }s[140000]; int t[52005],ans; void Build(int x,int y,int num){ s[num].a=x; s[num].b=y; if(x==y) s[num].sum=t[x]; else{ Build(x,(x+y)/2,num+num); Build(((x+y)/2)+1,y,num+num+1); s[num].sum=s[num+num].sum+s[num+num+1].sum; } } void Query(int x,int y,int num){ if(x<=s[num].a&&y>=s[num].b) ans+=s[num].sum; else{ if(x>(s[num].a+s[num].b)/2) Query(x,y,num+num+1); else if(y<=(s[num].a+s[num].b)/2) Query(x,y,num+num); else{ Query(x,y,num+num); Query(x,y,num+num+1); } } } void Add(int x,int y,int num){ s[num].sum+=y; if(s[num].a==x&&s[num].b==x) return ; else{ if(x<=(s[num].a+s[num].b)/2) Add(x,y,num+num); else Add(x,y,num+num+1); } } int Sub(int x,int y,int num){ s[num].sum-=y; if(s[num].a==x&&s[num].b==x) return 0; else{ if(x<=(s[num].a+s[num].b)/2) Sub(x,y,num+num); else Sub(x,y,num+num+1); } } int main(){ int cas,n,i,k,x,y; char c[20]; scanf("%d",&cas); k=1; while(cas--){ scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%d",&t[i]); } Build(1,n,1); cout<<"Case "<<k<<":"<<endl; k++; while(1){ scanf("%s",c); if(c[0]=='E') break; scanf("%d %d",&x,&y); if(c[0]=='Q'){ ans=0; Query(x,y,1); cout<<ans<<endl; } else if(c[0]=='A'){ Add(x,y,1); } else{ if(c[0]=='S') Sub(x,y,1); } } } return 0; }
HDU 2852(KiKi's K-Number)
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #define max 100000 #define lowbit(x) (x&(-x)) using namespace std; const int N=100001; int c[100005],a[100005]; int getsum(int x){ int sum=0; while(x){ sum+=c[x]; x-=lowbit(x); } return sum; } int modify(int x,int num){ while(x<=N){ c[x]+=num; x+=lowbit(x); } return 0; } int main(){ int n,i,left,right,md; int sign,m,t,num1; while(scanf("%d",&n)!=EOF){ memset(c,0,sizeof(c)); memset(a,0,sizeof(a)); while(n--){ scanf("%d",&sign); if(sign==0){ scanf("%d",&t); a[t]++; modify(t,1); } else if(sign==1){ scanf("%d",&t); if(!a[t]) cout<<"No Elment!"<<endl; else{ a[t]--; modify(t,-1); } } else if(sign==2){ scanf("%d %d",&m,&t); num1=getsum(m)+t; if(num1>getsum(max)){ cout<<"Not Find!"<<endl; continue; } left=m+1; right=max; while(right-left>1){ md=getsum((right+left)/2); if(md>num1){ right=(right+left)/2; } else if(md<num1){ left=(right+left)/2; } else { if(a[(right+left)/2]){ cout<<(right+left)/2<<endl; break; } else right=(right+left)/2; } } if(right-left<=1){ if(getsum(left)>=num1) cout<<left<<endl; else cout<<right<<endl; } } } } return 0; }
POJ 1990(MooFest)经典题目
题目概括:由n头牛,不同的听力值v,当i,j想要通话时,需要max(v(i),v(j))*(dist[i]-dist[j])的volume,问这n*(n-1)/2对牛总共的volume时多少。
题目分析:这里先把各个牛的信息都存放在结构体数组(s[i])里面,然后对这个结构体排序,按volume升序排序。因为这样可以保证先插入的必定比当前这头牛的volume小!首先解决了题目的一个要求,然后就是有效的计算出,各个牛距离当前牛的dist距离和,首先利用树状数组,计算出左边牛的头数(count)和左边牛的x轴坐标和(total);然后计算出整个区间上牛的坐标和(alltotal)。
根据公式:sum+= s[i].v* [ s[i].x * count - total + alltotal - ( i- count - 1 ) * s[i].x ] ;
源程序+部分注释:
#include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> #define lowbit(x) (x&(-x)) #define N 20020 using namespace std; struct node{ long long v; int x; }s[N]; int cmp(struct node a,struct node b){ return a.v<b.v; } long long sim[N],c[N]; long long getsum(long long t[],int x){ //因为每个函数都要进行两个操作,分别在不同数组里建图 //所以要把数组作为参量写入! long long sum=0; while(x>0){ sum+=t[x]; x-=lowbit(x); } return sum; } int modify(long long t[],int x,long long num){ while(x<=N){ t[x]+=num; x+=lowbit(x); } return 0; } int main(){ long long sum,total,alltotal,count; int i,cas; while(scanf("%d",&cas)!=EOF){ sum=0; memset(c,0,sizeof(c)); memset(sim,0,sizeof(sim)); for(i=1;i<=cas;i++){ scanf("%lld %d",&s[i].v,&s[i].x); } sort(s+1,s+1+cas,cmp); for(i=1;i<=cas;i++){ alltotal=getsum(c,20001); total=getsum(c,s[i].x); count=getsum(sim,s[i].x); sum+=s[i].v*(count*s[i].x-total+alltotal-total-s[i].x*(i-count-1)); modify(c,s[i].x,s[i].x); modify(sim,s[i].x,1); } printf("%lld\n",sum); } return 0; }
POJ 2155(Matrix)非常规思路
颠覆树状数组的常规模式,这题有些地方还有疑点!关于怎么计算点的翻转次数,为什么是他前面比他大的个数呢?回来再好好想想!
#include<iostream>
#include<stdio.h>
#include<string.h>
#define N 2001
#define lowbit(x) (x&(-x))
using namespace std;
int c[N][N];
int UPdate(int x,int y,int num){
int k;
while(x>0){
k=y;
while(k>0){
c[x][k]^=num;
k-=lowbit(k);
}
x-=lowbit(x);
}
return 0;
}
int getsum(int x,int y){
int sum=0,k;
while(x<=N){
k=y;
while(k<=N){
sum^=c[x][k];
k+=lowbit(k);
}
x+=lowbit(x);
}
return sum;
}
int main(){
int cas,i,n,q;
int x1,x2,y1,y2;
char sign;
cin>>cas;
while(cas--){
memset(c,0,sizeof(c));
scanf("%d %d",&n,&q);
while(q--){
cin>>sign;
//getchar();
if(sign=='C'){
cin>>x1>>y1>>x2>>y2;
x1++; x2++; y1++; y2++;
//printf("%d %d %d %d",x1,y1,x2,y2);
UPdate(x2,y2,1); UPdate(x1-1,y2,1);
UPdate(x2,y1-1,1); UPdate(x1-1,y1-1,1);
}
if(sign=='Q'){
scanf("%d %d",&x1,&y1);
x1++; y1++;
printf("%d\n",1&getsum(x1,y1));
}
}
printf("\n");
}
return 0;
}
POJ 2029(Get Many Persimmon Trees)
题目大意:一个H * W的大矩形,里面的某些格子种有树。现在要你找出一个h * w的小矩形,使得里面树的数量最多,问最多有多少棵树
题目分析:就是求二维的矩形和,然后枚举各个满足条件矩形和的最大值。
源程序+部分注释:
#include<iostream> #include<stdio.h> #include<string.h> #define lowbit(x) (x&(-x)) using namespace std; int w,h; int c[120][120]; int getsum(int x,int y){ int i=x,j,sum=0; while(i){ j=y; while(j){ sum+=c[i][j]; j-=lowbit(j); } i-=lowbit(i); } return sum; } int modify(int x,int y){ int k; while(x<=w){ k=y; while(k<=h){ c[x][k]+=1; k+=lowbit(k); } x+=lowbit(x); } return 0; } int count_sum(int x,int y,int xm,int ym){ int sum=0; sum=getsum(x,y)-getsum(x-xm,y)-getsum(x,y-ym)+getsum(x-xm,y-ym); return sum; } int main(){ int n,s,t; int i,j,x,y,max,ans; while(scanf("%d",&n),n){ memset(c,0,sizeof(c)); max=0; scanf("%d %d",&w,&h); for(i=1;i<=n;i++){ scanf("%d %d",&x,&y); modify(x,y); } scanf("%d %d",&s,&t); for(i=s;i<=w;i++) for(j=t;j<=h;j++){ ans=count_sum(i,j,s,t); if(max<ans) max=ans; } cout<<max<<endl; } return 0; }
POJ 3067(Japan)树状数组求线的交点
题目大意: 顺序给两组平行的点依次编号1~N和1~M,给定K个线段在两组点之间,求相交(cross)的线段对有多少个,同一个起点或终点不算相交
题目分析:把N从大到小排序,注意:当N相等的时候按M的从大到到小!因为我们计算的时候,求N点构成的交点的个数只需要遍历他前面的点对,因为他前面的点对的N值肯定比他大,所以只要M的值比他小肯定就构成了1个交点,所以转换为计算比当前值小的数的个数!这就是树状数组的真谛!然后如果当N相等的时候时候,如果把M小的放前面,计算他后面那个N值相等的点的时候,前面M比他小错误的加上了1,所以当N相等的时候必须按M从大到小的安放!
源程序+部分注释:
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #define lowbit(x) (x&(-x)) using namespace std; long long c[1010]; const int N=1005; struct node{ int w; int v; }s[1000002]; bool cmp(struct node a,struct node b){ if(a.w==b.w) return a.v>b.v; else return a.w>b.w; } //关键在排序注意一下! long long getsum(int x){ long long sum=0; while(x){ sum+=c[x]; x-=lowbit(x); } return sum; } int modify(int pos){ while(pos<=N){ c[pos]+=1; pos+=lowbit(pos); } return 0; } int main(){ int cas,k,r,m ,i,t; scanf("%d",&cas); while(cas--){ k=1; scanf("%d %d %d ",&r,&m,&t); memset(c,0,sizeof(c)); long long ans=0; for(i=1;i<=t;i++) scanf("%d %d",&s[i].w,&s[i].v); sort(s+1,s+1+t,cmp); for(i=1;i<=t;i++){ ans+=getsum(s[i].v-1); modify(s[i].v); } printf("Test case %d: %lld \n", k++, ans); } return 0; }
POJ 2481(Cows)树状数组
题目分析:题意大概是这样的,给一些组数据,每i组有两个数S(i)和 E(i),对于每组,求出有多少组满足 S(t) <= S(i), E(t) >= E(i), E(t)-S(t) > E(i)-S(i);
首先将S[i]降序排序,如果相等则按E[i]的升序。除去特殊情况,所有都满足公式!如果遇到完全重合的数据,则单独考虑,也就是不需求和计算,只需把前面的值赋值给他,
#include<iostream> #include<stdio.h> #include<algorithm> #include<string.h> #define lowbit(x) (x&(-x)) #define N 100010 using namespace std; int c[100002],sim[100002]; struct node{ int s; int order; int e; }s[100002]; bool cmp(struct node a,struct node b){ if(a.e!=b.e) return a.e>b.e; else return a.s<b.s; } int getsum(int x){ int sum=0; while(x>0){ sum+=c[x]; x-=lowbit(x); } return sum; } int modify(int pos,int num){ while(pos<N){ c[pos]+=num; pos+=lowbit(pos); } return 0; } int main(){ int cas,i,st,et; while(~scanf("%d",&cas)&&cas){ memset(c,0,sizeof(c)); for(i=1;i<=cas;i++){ scanf("%d%d",&s[i].s,&s[i].e); s[i].s++; s[i].e++; s[i].order=i; } sort(s+1,s+1+cas,cmp); int ans=getsum(s[1].s); sim[s[1].order]=ans; modify(s[1].s,1); st=s[1].s; et=s[1].e; for(i=2;i<=cas;i++){ if(s[i].s==st&&s[i].e==et){ sim[s[i].order]=sim[s[i-1].order]; //重复的就不需要求和,直接把前面的给他 modify(s[i].s,1); //但是还得修改哦{^_^} continue; } st=s[i].s; et=s[i].e; int ans=getsum(s[i].s); sim[s[i].order]=ans; modify(s[i].s,1); } for(i=1; i<=cas; i++) { if( i != 1 ) printf(" "); printf("%d",sim[i]); } printf("\n"); } return 0; }
POJ 1195(Mobile phones)简单二维树状数组
题目大意:给定n*n矩阵,和几种在线操作,包括对某一点(x,y)值修改,查询一个矩形(x1,y1,x2,y2)的元素和。
题目思路:在这里稍微注意一下,每个输入的坐标都加上1,这样防止有0现象发生!Lowbit(0) = 0,这会导致x递增的那条路径发生死循环!求和的公式sum(x2-x1,y2-y1)=getsum(x2,y2)-getsum(x1-1,y2)-getsum(x2,y1-1)+getsum(x1-1,y1-1);
源程序+部分注释:
#include<iostream> #include<string.h> #include<stdio.h> #define lowbit(x) (x&(-x)) using namespace std; int c[1050][1050]; int N; int getsum(int x,int y){ int sum=0; int j; while(x){ j=y; while(j){ sum+=c[x][j]; j-=lowbit(j); } x-=lowbit(x); } return sum; } int modify(int i,int j ,int num){ int k; while(i<=N){ k=j; while(k<=N){ c[i][k]+=num; k+=lowbit(k); } i+=lowbit(i); } return 0; } int main(){ int i,x1,y1,x2,y2; int cas,a,b,num,t,j; scanf("%d %d",&cas,&N); memset(c,0,sizeof(c)); while(scanf("%d",&t),t!=3){ if(1==t){ scanf("%d%d%d",&a,&b,&num); modify(a+1,b+1,num); } else { if(2==t){ scanf("%d %d %d %d",&x1,&y1,&x2,&y2); x1++; y1++; x2++; y2++; //防止0现象,造成死循环! printf("%d\n",getsum(x2,y2)-getsum(x1-1,y2)-getsum(x2,y1-1)+getsum(x1-1,y1-1)); //这个公式就是去除多余的部分,加上在去除过程中重复减去的部分,正好等于所求区域 } } } return 0; }
POJ 2352(Stars)树状数组
题目分析:就是让您求位于没颗星星左下方的包括自己正下方的星星个数。因为他已经按Y轴排好序的输入了,所以只需要按着顺序在X轴上建立树状数组就可以了。
#include<iostream> #include<stdio.h> #define lowbit(x) (x&(-x)) using namespace std; const int N=32100; int c[32100],level[15010]; int getsum(int x){ int sum=0; while(x>0){ sum+=c[x]; x-=lowbit(x); } return sum; } int modify(int pos,int num){ while(pos<=N){ c[pos]+=num; pos+=lowbit(pos); } return 0; } int main(){ int m, i,x,y,t; while(scanf("%d",&t)!=EOF){ m=t; for(i=0;i<=N;i++){ c[i]=0; } for(i=0;i<=15000;i++) level[i]=0; while(m--){ scanf("%d %d",&x,&y); level[getsum(x+1)]++; //用这个表记录leve出现的次数 modify(x+1,1); } for(i=0;i<t;i++) cout<<level[i]<<endl; } return 0; }
POJ 2299(Ultra-QuickSort)树状数组解逆序数
题目分析:这个题目就是用一个结构体数组,把每个数的值记录下来,还有他初始在数组里的位置,然后进行从小到大排序。
这样就够成了条件1)先插入的必定是值比前面大的!然后每次进行求和,2)只要初始位置也就是坐标轴上的X在我前面的个数就是当前这个数构成逆序的对数。
#include<iostream> #include<stdio.h> #include<algorithm> #include<cstring> using namespace std; inline int lowbit(int x){ return x&(-x); } long long c[500001]; long long sum; struct node{ int num,sert; }t[500001]; long long getsum(int n){ int ans=0; while(n>0){ ans+=c[n]; n-=lowbit(n); } return ans; } int modify(int num,int count){ while(num<500001){ c[num]+=count; num+=lowbit(num); } return 0; } int cmp(struct node a,struct node b){ return a.num>b.num; } int main(){ int i,N; while(scanf("%d",&N),N){ sum=0; memset(c,0,sizeof(c)); for(i=1;i<=N;i++){ cin>>t[i].num; t[i].sert=i; } sort(t+1,t+1+N,cmp); for(i=1;i<=N;i++){ sum+=getsum(t[i].sert); //先求和在修改,这样因为第一个插入的肯定 //为0,符号实际意义 modify(t[i].sert,1); //修改的时候只需要把该位置改为1,也就是代表个数 } cout<<sum<<endl; } return 0; }