入门篇:http://fqq11679.blog.hexun.com/21722866_d.html
hdu1166 敌兵布阵 【单点更新,区间求和】
http://acm.hdu.edu.cn/showproblem.php?pid=1166
最基本的模板题
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
int n;
void update(int *a,int x,int w)
{
while(x<=n)
{
a[x]+=w;
x+=lowbit(x);
}
}
int sum(int *a,int x)
{
int ans=0;
while(x>0)
{
ans+=a[x];
x-=lowbit(x);
}
return ans;
}
int main()
{
int a[50001],b[50001];
int T,t=1;
scanf("%d",&T);
while(T--)
{
int w;
scanf("%d",&n);
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++)
{
scanf("%d",&w);
update(a,i,w);
}
printf("Case %d:\n",t++);
char s[10];
int x,y;
scanf("%s",&s);
while(strcmp(s,"End")!=0)
{
if(strcmp(s,"Add")==0)
{
scanf("%d%d",&x,&y);
update(a,x,y);
}
else if(strcmp(s,"Sub")==0)
{
scanf("%d%d",&x,&y);
update(a,x,-y);
}
else if(strcmp(s,"Query")==0)
{
scanf("%d%d",&x,&y);
int ans=sum(a,y)-sum(a,x-1);
printf("%d\n",ans);
}
scanf("%s",&s);
}
}
return 0;
}
poj3928 Ping pong【单点更新,区间求和】
http://poj.org/problem?id=3928
题意:一条大街上从西到东依次住着N个乒乓球爱好者,他们经常举办比赛。每个人都有一个不同的技能值。每场比赛需要三个人,一个裁判加两个爱好者,不过比赛有一个要求就是裁判的技能值需要在两个爱好者的中间,并且居住的位置也要求在两个爱好者的中间,问总共能够举办多少场比赛。
思路:枚举每个人为裁判。ls[i]为裁判左边比他技能值小的人数,ll[i]为裁判左边比他技能值大的人数,rs[i]为裁判左边比他技能值小的人数,rl[i]为裁判右边比他技能值大的人数。则ls[i]*rl[i]+ll[i]*rs[i]即为所求结果。
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
const int INF=0x3f3f3f3f;
long long ans;
int T,n,maxx;
int a[100001],ll[20001],ls[20001],rl[20001],rs[20001],rank[20001];//ll:左大,ls:左小,rl:右大,rs:右小
void update(int *a,int x,int w)
{
while(x<=maxx)
{
a[x]+=w;
x+=lowbit(x);
}
}
int sum(int *a,int x)
{
int ans=0;
while(x>0)
{
ans+=a[x];
x-=lowbit(x);
}
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
maxx=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&rank[i]);
if(rank[i]>maxx)
maxx=rank[i];
}
memset(a,0,sizeof(a));
memset(ll,0,sizeof(ll));
memset(ls,0,sizeof(ls));
for(int i=1;i<=n;i++)
{
ls[i]=sum(a,rank[i]);
ll[i]=i-1-ls[i];
update(a,rank[i],1);
}
memset(a,0,sizeof(a));
memset(rl,0,sizeof(rl));
memset(rs,0,sizeof(rs));
for(int i=n;i>0;i--)
{
rs[i]=sum(a,rank[i]);
rl[i]=n-i-rs[i];
update(a,rank[i],1);
}
ans=0;
for(int i=1;i<=n;i++)
ans+=(long long)ls[i]*rl[i]+(long long)ll[i]*rs[i];
printf("%I64d\n",ans);
}
return 0;
}
poj2299 Ultra-QuickSort【单点更新,区间求和】
http://poj.org/problem?id=2299
题意:给你一个数列,每次只能交换相邻的两个数,问多少次交换可以将数列从小到大排序
思路:其实就是求逆序数个数。枚举每个数,之前比它大的数的个数加起来就是逆序数个数。由于数值范围是999999999,但个数只有500000,所以需要先离散化。
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
const int maxn=500001;
struct point
{
int value,pos;
};
int n;
int c[maxn],f[maxn];
long long ans;
point a[maxn];
void update(int *a,int x,int w)
{
while(x<=n)
{
a[x]+=w;
x+=lowbit(x);
}
}
int sum(int *a,int x)
{
int ans=0;
while(x>0)
{
ans+=a[x];
x-=lowbit(x);
}
return ans;
}
void quicksort(int s,int t)
{
int i,j,x,g;
point k;
i=s; j=t; g=(s+t)>>1; x=a[g].value;
while(i<=j)
{
while(a[i].value<x) i++;
while(a[j].value>x) j--;
if(i<=j)
{
k=a[i]; a[i]=a[j]; a[j]=k;
i++; j--;
}
}
if(s<j) quicksort(s,j);
if(i<t) quicksort(i,t);
}
int main()
{
scanf("%d",&n);
while(n!=0)
{
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].value);
a[i].pos=i;
}
quicksort(1,n);
for(int i=1;i<=n;i++) //离散化
f[a[i].pos]=i;
memset(c,0,sizeof(c));
ans=0;
for(int i=1;i<=n;i++)
{
ans+=(long long)i-1-sum(c,f[i]);
update(c,f[i],1);
}
printf("%I64d\n",ans);
scanf("%d",&n);
}
return 0;
}
poj2481 Cows 【单点更新,区间求和】
http://poj.org/problem?id=2481
题意:有n头奶牛,每头奶牛覆盖一段区间[l,r]的牧草。若对于两只奶牛,l1<=l2,r1>=r2,r1-l1>r2-l2,则说奶牛1比奶牛2强。现在给你每头奶牛的牧草区域,问对于每头奶牛,有多少比它强的。
思路:离线。将奶牛的覆盖区域按l升序,r降序排列。那么对于每个r,之前只要出现一个比它大的ri,必定有li,ri覆盖的区域比l,r大(或者相等,特判),维护一个树状数组,对于每个r,看之前有多少个比它大的r即可。
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
struct point
{
int l,r,id;
bool operator < (const point & p) const
{
if(l==p.l) return r>p.r;
else return l<p.l;
}
};
const int maxn=100010;
int n,maxx;
int c[maxn],ans[maxn];
point a[maxn];
void update(int *a,int x,int w)
{
while(x<=maxx)
{
a[x]+=w;
x+=lowbit(x);
}
}
int sum(int *a,int x)
{
int ans=0;
while(x>0)
{
ans+=a[x];
x-=lowbit(x);
}
return ans;
}
int main()
{
scanf("%d",&n);
while(n!=0)
{
maxx=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].l,&a[i].r);
a[i].id=i;
if(a[i].r>maxx)
maxx=a[i].r;
}
sort(a+1,a+n+1);
memset(c,0,sizeof(c));
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;i++)
{
if(i!=1 && a[i].l==a[i-1].l && a[i].r==a[i-1].r)
ans[a[i].id]=ans[a[i-1].id];
else
ans[a[i].id]=i-1-sum(c,a[i].r-1);
update(c,a[i].r,1);
}
for(int i=1;i<=n-1;i++)
printf("%d ",ans[i]);
printf("%d\n",ans[n]);
scanf("%d",&n);
}
return 0;
}
hdu4638 Group【单点更新,区间查询】
http://acm.hdu.edu.cn/showproblem.php?pid=4638
题意:给你一串数,然后对于每次询问l和r,问你在[l,r]中有多少段连续的数
思路:树状数组,离线。每向后添加一个i位置的数a[i],先直接在这个位置+1,表示出现一个新组,然后看前面是否出现a[i]-1和a[i]+1,若出现a[i]-1,则在pos[a[i]-1]位置-1,a[i]+1同理。因为此时数段被合并起来了,a[i]就代表了它们的状态。将所有的询问按r从小到大排序。然后枚举i,树状数组维护的是第i个数之前那些-1,0,1的和。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
const int maxn=100001;
struct point
{
int l,r,id;
bool operator < (const point & p) const
{
return r<p.r;
}
};
int T,n,m;
int a[maxn],c[maxn],pos[maxn],ans[maxn];
bool mark[maxn];
point q[maxn];
void update(int *a,int x,int w)
{
while(x<=n)
{
a[x]+=w;
x+=lowbit(x);
}
}
int sum(int *a,int x)
{
int ans=0;
while(x>0)
{
ans+=a[x];
x-=lowbit(x);
}
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[a[i]]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q+1,q+m+1);
memset(mark,0,sizeof(mark));
memset(c,0,sizeof(c));
int j=1;
for(int i=1;i<=n;i++)
{
mark[a[i]]=1;
update(c,pos[a[i]],1);
if(a[i]-1>0 && mark[a[i]-1])
update(c,pos[a[i]-1],-1);
if(a[i]+1<=n && mark[a[i]+1])
update(c,pos[a[i]+1],-1);
while(q[j].r==i)
{
ans[q[j].id]=sum(c,q[j].r)-sum(c,q[j].l-1);
j++;
}
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
return 0;
}