洛谷 P3374 【模板】树状数组 1
单点修改,区间查询和。
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m,x,y,opt,a[N];
void update(int i,int v)
{
while(i<=n)
{
a[i]=a[i]+v;
i=i+(i&-i);
}
}
int sum(int i)
{
int s=0;
while(i)
{
s=s+a[i];
i=i-(i&-i);
}
return s;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>x;
update(i,x);
}
while(m--)
{
cin>>opt>>x>>y;
if(opt==1)update(x,y);
else printf("%d\n",sum(y)-sum(x-1));
}
return 0;
}
洛谷 P3368 【模板】树状数组 2
区间修改,单点查询。维护差分数组即可。
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m,x,y,k,opt,a[N],ans[N];//原数组为a[i],其差分数组保存在树状数组ans[i]中
void update(int i,int v)
{
while(i<=n)
{
ans[i]=ans[i]+v;
i=i+(i&-i);
}
}
int sum(int i)
{
int s=0;
while(i)
{
s=s+ans[i];
i=i-(i&-i);
}
return s;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
x=a[i]-a[i-1];//求差分
update(i,x);
}
while(m--)
{
cin>>opt;
if(opt==1)
{
cin>>x>>y>>k;
update(x,k);
update(y+1,-k);
}
else
{
cin>>x;
printf("%d\n",sum(x));
}
}
return 0;
}
LOJ 10117「一本通 4.1 练习 2」简单题
和上题类似,区间修改,单点询问。
用树状数组tr[]维护差分数组,从而记录区间内每个点反转的次数。
原数组初始值全为0,则其差分数组初始值也全为0,sum(i)表示差分数组1~i的前缀和,即i点反转的次数。
[l,r]内所有点的反转次数加1,等价于差分数组的tr[l]+1且tr[r+1]-1。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,x,l,r,opt,tr[N];
void add(int i,int k)
{
while(i<=n)
{
tr[i]+=k;
i+=(i&-i);
}
}
int sum(int i)
{
int s=0;
while(i)
{
s+=tr[i];
i-=(i&-i);
}
return s;
}
int main()
{
ios::sync_with_stdio(false);
while(cin>>n>>m)
{
memset(tr,0,sizeof(tr));
while(m--)
{
cin>>opt;
if(opt==1)
{
cin>>l>>r;
add(l,1);
if(r+1<=n)add(r+1,-1);
}
else
{
cin>>x;
if(sum(x)&1)printf("1\n");
else printf("0\n");
}
}
}
return 0;
}
nefu 480 询问区间和
注意a[i]可能等于0,所以当顾客买走a[i]时,不能令a[i]=0当成标记数组用(价值为0的珠宝被买走了也要输出“0”,而不是输出“Sorry”)。要另外开一个标记数组。
还有就是注意树状数组的下标只能从1开始,这题输入的下标是从0开始的,所以要把输入的下标+1。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll tr[N];
int n,m,l,r,x,cas,opt,a[N],flag[N];
void add(int i,int k)
{
while(i<=n)
{
tr[i]+=k;
i+=(i&-i);
}
}
ll sum(int i)
{
ll s=0;
while(i)
{
s+=tr[i];
i-=(i&-i);
}
return s;
}
int main()
{
ios::sync_with_stdio(false);
while(cin>>n>>m)
{
memset(tr,0,sizeof(tr));
memset(flag,0,sizeof(flag));
for(int i=1;i<=n;i++)
{
cin>>a[i];
add(i,a[i]);
}
printf("Case #%d:\n",++cas);
while(m--)
{
cin>>opt;
if(opt==0)
{
cin>>x;x++;
if(flag[x])printf("Sorry\n");
else
{
printf("%d\n",a[x]);
add(x,-a[x]);
flag[x]=1;
}
}
else
{
cin>>l>>r;
l++;r++;
printf("%lld\n",sum(r)-sum(l-1));
}
}
}
return 0;
}
nefu 479 神奇的字符串
树状数组维护两个字符串中相同的字符个数即可。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
char c,s1[N],s2[N];
int n,m,p,l,r,id,opt,cas,tr[N];
void update(int i,int k)//在i位置加上一个数k
{
while(i<=n)
{
tr[i]+=k;
i+=(i&-i);
}
}
int sum(int i)
{
int s=0;
while(i)
{
s+=tr[i];
i-=(i&-i);
}
return s;
}
int main()
{
ios::sync_with_stdio(false);
while(cin>>n>>m)
{
memset(tr,0,sizeof(tr));
for(int i=1;i<=n;i++)
cin>>s1[i];
for(int i=1;i<=n;i++)
{
cin>>s2[i];
if(s1[i]==s2[i])update(i,1);
}
printf("Case #%d:\n",++cas);
while(m--)
{
cin>>opt;
if(opt==0)
{
cin>>l>>r;
printf("%d\n",sum(r)-sum(l-1));
}
else
{
cin>>id>>p>>c;
if(id==1)//把s1的p位置修改为c
{
if(s1[p]==s2[p]&&c!=s1[p])update(p,-1);
if(s1[p]!=s2[p]&&c==s2[p])update(p,1);
s1[p]=c;
}
else//把s2的p位置修改为c
{
if(s1[p]==s2[p]&&c!=s1[p])update(p,-1);
if(s1[p]!=s2[p]&&c==s1[p])update(p,1);
s2[p]=c;
}
}
}
}
return 0;
}
洛谷 P1908 逆序对
把a[i]当作数轴上的下标, 将a[i]离散化后存入树状数组中,由于是按时间顺序存入的,所以大于当前遍历的a[i]的所有数均与a[i]构成逆序对,即sum(n)-sum(a[i])(这里的n是a[i]的最大值,因为a[i]经过了离散化,所以a[i]的最大值不超过n)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+10;
ll ans,tr[N];
int n,cnt,a[N],t[N];
void add(int i)
{
while(i<=n)
{
tr[i]++;
i+=(i&-i);
}
}
ll sum(int i)
{
ll s=0;
while(i)
{
s+=tr[i];
i-=(i&-i);
}
return s;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
t[i]=a[i];
}
sort(t+1,t+n+1);
cnt=unique(t+1,t+n+1)-t-1;//是-t-1
for(int i=1;i<=n;i++)
{//将a[i]离散化
a[i]=lower_bound(t+1,t+cnt+1,a[i])-t;//是-t,不是-t-1
add(a[i]);
ans+=sum(n)-sum(a[i]);
}
printf("%lld\n",ans);
return 0;
}
poj 2352 Stars
把x坐标离散化后存入树状数组维护即可。
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=15010;
int n,y,cnt,x[N],t[N],tr[N],ans[N];
void add(int i,int k)
{
while(i<=n)
{
tr[i]+=k;
i+=(i&-i);
}
}
int sum(int i)
{
int s=0;
while(i)
{
s+=tr[i];
i-=(i&-i);
}
return s;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>x[i]>>y;
t[i]=x[i];
}
sort(t+1,t+n+1);
cnt=unique(t+1,t+n+1)-t-1;
for(int i=1;i<=n;i++)
{
x[i]=lower_bound(t+1,t+cnt+1,x[i])-t;//将x离散化
ans[sum(x[i])]++;//第sum(x[i])级的个数+1
add(x[i],1);
}
for(int i=0;i<n;i++)
printf("%d\n",ans[i]);
return 0;
}
洛谷 P2345 奶牛集会
想办法化简公式,去掉原公式中的max和绝对值符号。
第i头牛和其他的牛发出的声音 = 第i头牛的vi * (它之前牛的个数 * 它的坐标 - 它之前所有牛的坐标和) + 第i头牛的vi * (它之后所有牛的坐标和 - 它之后牛的个数 * 它的坐标)。
对于牛在某个坐标区间内的个数和牛的坐标和,我们可以用树状数组来维护。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e4+10;
int v,x,n;
ll ans,tre1[N],tre2[N];
struct node
{
int v,x;
}a[N];
bool cmp(node a,node b)
{
if(a.v==b.v)return a.x<b.x;
return a.v<b.v;
}
void add1(int i,int k)//维护个数
{
while(i<=N)//是i<=N不是i<=n
{
tre1[i]+=k;
i+=(i&-i);
}
}
ll cnt(int i)//维护个数
{
ll s=0;
while(i)
{
s+=tre1[i];
i-=(i&-i);
}
return s;
}
void add2(int i,int k)//维护坐标和
{
while(i<=N)//是i<=N不是i<=n
{
tre2[i]+=k;
i+=(i&-i);
}
}
ll sum(int i)//维护坐标和
{
ll s=0;
while(i)
{
s+=tre2[i];
i-=(i&-i);
}
return s;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i].v>>a[i].x;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
v=a[i].v;
x=a[i].x;
add1(x,1);
add2(x,x);
ans+=v*(x*cnt(x-1)-sum(x-1)+sum(N)-sum(x)-x*(cnt(N)-cnt(x)));
}
printf("%lld\n",ans);
return 0;
}