单点更新
HDU 2795 Billboard
val[rt] 表示第rt棵树可用长度,初始化全为最大长度,要更新时就减去要贴上的海报长度,更新查询只用到叶子结点,所以我们把父节点设为max( val [左],val [右] )。
h再大,最多也只需要n行就可以贴上(贴的长度大于墙长度除外),所以h > n 时 h = n即可
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define MOD 10009
#define bug(x) cout<<#x<<":"<<(x)<<endl;
#define Mid ((l+r)>>1)
#define lson rt<<1,l,Mid
#define rson rt<<1|1,Mid+1,r
const int maxn = 200010;
int val[maxn<<2];
void build(int rt,int l,int r,int w)
{
if(l == r){
val[rt] = w;
}else{
build(lson,w);
build(rson,w);
val[rt]=max(val[rt<<1],val[rt<<1|1]);
}
}
int ans;
void query(int rt,int l,int r,int z)
{
if(l == r){
val[rt] -= z;
ans = l;
}else{
if(val[rt<<1] >= z)
query(lson,z);
else
query(rson,z);
val[rt] = max(val[rt<<1],val[rt<<1|1]);
}
}
int main()
{
// RE;
int t,n,h,w,tmp;
while(scanf("%d%d%d",&h,&w,&n)!=EOF){
if(h > n)
h = n;
build(1,1,h,w);
while(n--){
scanf("%d",&tmp);
if(tmp > val[1]){
printf("-1\n");
continue;
}
query(1,1,h,tmp);
printf("%d\n",ans);
}
}
return 0;
}
HDU 1394 Minimum Inversion Number
先求逆序数,可以线段树也可以树状数组,归并等求,其实这题关键的是结论:0到n的排列,把第一个数放到最后,逆序数是减少a[i],增加n-1-a[i]的
#include <cstdio>
#include <cmath>
#include <cstring>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define INF 0x7FFFFFFF
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) )
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define lson i<<1,l,m
#define rson i<<1|1,m+1,r
const int maxn=5010;
struct node
{
int l,r;
int mid(){
return (l+r)>>1;
}
}tree[maxn<<2];
int sum[maxn<<2],add[maxn<<2];
int a[maxn];
void pushUp(int i)
{
sum[i]=sum[i<<1]+sum[i<<1|1];
}
void build(int i,int l,int r)
{
tree[i].l=l,tree[i].r=r;
sum[i]=0;
if(l==r)
return;
int m=tree[i].mid();
build(i<<1,l,m);
build(i<<1|1,m+1,r);
}
void update(int i,int k)
{
sum[i]++;
if(tree[i].l==tree[i].r)
{
return;
}
int m=tree[i].mid();
if(k<=m)
update(i<<1,k);
else //if(k>m)
update(i<<1|1,k);
pushUp(i);
}
int query(int i,int l,int r)
{
if(l>r) return 0;
if(tree[i].l==l&&tree[i].r==r)
return sum[i];
int m=tree[i].mid();
if(r<=m)
return query(i<<1,l,r);
else if(l>m)
return query(i<<1|1,l,r);
else
return query(i<<1,l,m)+query(i<<1|1,m+1,r);
}
int main()
{
// RE;
int t,n,q,b,c,x,y,z,op;
char s1[5];
char ch;
while(cin>>n)
{
int ans=0;
build(1,1,n);
FOR(i,0,n-1)
{
cin>>a[i];
ans+=query(1,1+a[i],n-1); //0开始,所以加1
update(1,a[i]);
}
int tmp=ans;
FOR(i,0,n-1)
{
ans-=a[i];
ans+=n-a[i]-1;
tmp=min(tmp,ans);
}
cout<<tmp<<endl;
}
return 0;
}
POJ 2828 Buy Tickets这题比较好,看起来不太像线段树
题意:全部人都插队,n个人,输入他插队的位置(如1就在第1个人后面,0就是队首)和这个人的编号,按照输入顺序插队,求最终队伍顺序
思路:因为目前这个人插的位置可能会被后面的人插进来,所以我们可以逆序插入线段树,因为最后进来的肯定是最终位置。插入的时候查询前面有多少个空位就行,如果刚好就插在那,如果某个点左子树空位多于等于目标,就去左子树找,略像搜索;如果左边少了,则去右子树找当前所需空位数-左子树空位数的空位。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define INF 0x7FFFFFFF
#define INT_MIN -(1<<31)
#define eps 10^(-6)
#define Q_CIN ios::sync_with_stdio(false);
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) );
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define MOD 10009
#define debug(x) cout<<#x<<":"<<(x)<<endl;
#define lson i<<1,l,m
#define rson i<<1|1,m+1,r
const int maxn=200010;
int sum[maxn<<2]; //1的个数
int ans[maxn]; //存答案
int loc[maxn],num[maxn];
void pushUp(int i)
{
sum[i]=sum[i<<1]+sum[i<<1|1];
}
void build(int i,int l,int r)
{
sum[i]=0;
if(l==r)
{
sum[i]=1;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushUp(i);
}
void query(int i,int l,int r,int pos,int c)
{
if(l==r)
{
sum[i]=0;
ans[r]=c;
return;
}
int m=(l+r)>>1;
if(sum[i<<1]>=pos)
query(lson,pos,c);
else if(sum[i<<1]<pos) //要有else,因为如果左边找到了,就改变了sum[i<<1],可能符合下面条件
query(rson,pos-sum[i<<1],c);
pushUp(i);
}
int main()
{
RE;
ios::sync_with_stdio(false);
int n;
while(cin>>n)
{
build(1,1,n);
FOR(i,1,n)
cin>>loc[i]>>num[i];
for(int i=n;i>=1;i--)
query(1,1,n,loc[i]+1,num[i]);
FOR(i,1,n-1)
cout<<ans[i]<<" ";
cout<<ans[n]<<endl;
}
return 0;
}
区间更新
POJ 3648 A Simple Problem with Integers
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define MOD 10009
#define bug(x) cout<<#x<<":"<<(x)<<endl;
#define ll long long
#define Mid ((l+r)>>1)
#define lson rt<<1,l,Mid
#define rson rt<<1|1,Mid+1,r
const int maxn = 100010;
ll sum[maxn<<2],add[maxn<<2];
void build(int rt,int l,int r)
{
add[rt] = 0;
if(l == r){
scanf("%I64d",&sum[rt]);
}else{
build(lson);
build(rson);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
}
void pushDown(int rt,int len)
{
add[rt<<1] += add[rt];
add[rt<<1|1] += add[rt];
sum[rt<<1] += (len-(len>>1))*add[rt];
sum[rt<<1|1] += (len>>1)*add[rt];
add[rt] = 0;
}
void update(int rt,int l,int r,int L,int R,int z)
{
if(L <= l && r <= R){
add[rt] += z;
sum[rt] += (r-l+1)*z;
}else{
if(add[rt])
pushDown(rt,r-l+1);
if(L <= Mid)
update(lson,L,R,z);
if(R > Mid)
update(rson,L,R,z);
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
}
ll query(int rt,int l,int r,int L,int R)
{
if(L <= l && r <= R){
return sum[rt];
}else{
if(add[rt])
pushDown(rt,r-l+1);
ll t = 0;
if(L <= Mid)
t += query(lson,L,R);
if(R > Mid)
t += query(rson,L,R);
return t;
}
}
int main()
{
// RE
int L,R,z,n,m;
char op;
while(scanf("%d%d",&n,&m)!=EOF){
build(1,1,n);
while(m--){
scanf("%*c%c",&op);
if(op=='C'){
scanf("%d%d%d",&L,&R,&z);
update(1,1,n,L,R,z);
}else{
scanf("%d%d",&L,&R);
printf("%I64d\n",query(1,1,n,L,R));
}
}
}
}
POJ 2777 Count Color
'C':将区间涂色
'P':求区间内的颜色种类数
sum即颜色种类,懒得改变量名字了,颜色用int的每一位表示,如100表示有第3种颜色。父节点的种类 = 左节点 | 右节点,总种类为二进制里1的个数
#include <cstdio>
#include <cmath>
#include <cstring>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define INF 0x7FFFFFFF
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) )
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
const int maxn=100010;
struct node
{
int l,r;
int mid(){
return (l+r)>>1;
}
}tree[maxn<<2];
int sum[maxn<<2],add[maxn<<2];
void pushUp(int i)
{
sum[i]=sum[i<<1]|sum[i<<1|1];
}
void build(int i,int l,int r)
{
tree[i].l=l,tree[i].r=r;
add[i]=1;sum[i]=1;
if(l==r)
{
add[i]=1;
sum[i]=1;
return;
}
int m=tree[i].mid();
build(i<<1,l,m);
build(i<<1|1,m+1,r);
pushUp(i);
}
void pushDown(int i)
{
add[i<<1]=add[i<<1|1]=add[i];
sum[i<<1]=sum[i<<1|1]=sum[i];
add[i]=0;
}
void update(int i,int l,int r,int num)
{
if(tree[i].l==l&&tree[i].r==r)
{
add[i]=num;
sum[i]=(1<<(num-1)); //将第num为设置为1
return;
}
if(add[i])
pushDown(i);
int m=tree[i].mid();
if(r<=m)
update(i<<1,l,r,num);
else if(l>m)
update(i<<1|1,l,r,num);
else
{
update(i<<1,l,m,num);
update(i<<1|1,m+1,r,num);
}
pushUp(i);
}
int query(int i,int l,int r)
{
if(tree[i].l==l&&tree[i].r==r)
return sum[i];
if(add[i])
pushDown(i);
int m=tree[i].mid();
if(r<=m)
return query(i<<1,l,r);
else if(l>m)
return query(i<<1|1,l,r);
else
return query(i<<1,l,m)|query(i<<1|1,m+1,r);
}
int calc(int x) //计算x有多少位1
{
int num = 0;
while(x)
{
num += x&1;
x>>=1;
}
return num;
}
int main()
{
RE;
int t,n,q,a,b,c,x,y,z,op;
char s1[5];
char ch;
while(scanf("%d%d%d",&n,&t,&op)!=EOF)
{
build(1,1,n);
getchar();
while(op--)
{
scanf("%c",&ch);
if(ch=='C')
{
scanf("%d%d%d%*c",&x,&y,&z);
if(x>y)
swap(x,y);
update(1,x,y,z);
}else if(ch=='P')
{
scanf("%d%d%*c",&x,&y);
if(x>y)
swap(x,y);
int tmp = query(1,x,y);
printf("%d\n",calc(tmp));
}
}
}
return 0;
}
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define INF 0x7FFFFFFF
#define INT_MIN -(1<<31)
#define eps 10^(-6)
#define Q_CIN ios::sync_with_stdio(false);
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) );
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define MOD 10009
#define debug(x) cout<<#x<<":"<<(x)<<endl;
#define lson i<<1,l,m
#define rson i<<1|1,m+1,r
const int maxn=100010;
int sum[maxn<<2],add[maxn<<2];
void pushUp(int i)
{
sum[i]=sum[i<<1]|sum[i<<1|1];
}
void build(int i,int l,int r)
{
add[i]=0;
sum[i]=0;
if(l==r)
{
add[i]=1;
sum[i]=1;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushUp(i);
}
void pushDown(int i)
{
add[i<<1]=add[i<<1|1]=add[i];
sum[i<<1]=(1<<(add[i]-1));
sum[i<<1|1]=(1<<(add[i]-1));
// sum[i<<1]=sum[i<<1|1]=sum[i]; //在这里等同上面两句
add[i]=0;
}
void update(int i,int l,int r,int L,int R,int num)
{
if(L<=l&&r<=R)
{
add[i]=num;
sum[i]=(1<<(num-1)); //将第num为设置为1
return;
}
if(add[i])
pushDown(i);
int m=(l+r)>>1;
if(L<=m)
update(lson,L,R,num);
if(R>m)
update(rson,L,R,num);
pushUp(i);
}
int query(int i,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
return sum[i];
if(add[i])
pushDown(i);
int m=(l+r)>>1;
int t1=0,t2=0;
if(L<=m)
t1=query(lson,L,R);
if(R>m)
t2=query(rson,L,R);
return t1|t2;
}
int calc(int x) //计算x有多少位1
{
int num = 0;
while(x)
{
num += x&1;
x>>=1;
}
return num;
}
int main()
{
int t,n,q,a,b,c,x,y,z,op;
char s1[5];
char ch;
while(scanf("%d%d%d",&n,&t,&op)!=EOF)
{
build(1,1,n);
getchar();
while(op--)
{
scanf("%c",&ch);
if(ch=='C')
{
scanf("%d%d%d%*c",&x,&y,&z);
if(x>y) swap(x,y);
update(1,1,n,x,y,z);
}else if(ch=='P')
{
scanf("%d%d%*c",&x,&y);
if(x>y) swap(x,y);
int tmp = query(1,1,n,x,y);
printf("%d\n",calc(tmp));
}
}
}
return 0;
}
来个复杂点的,既有区间加值又有区间改值。
hihocoder 1080.更为复杂的买卖房屋姿势
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define eps 10^(-6)
#define Q_CIN ios::sync_with_stdio(false);
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) );
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define MOD 10009
#define debug(x) cout<<#x<<":"<<(x)<<endl;
#define lson i<<1,l,m
#define rson i<<1|1,m+1,r
int sum[100010<<2],add[100010<<2],eva[100010<<2];
int n,m;
void build(int i,int l,int r)
{
add[i]=0,eva[i]=0;
if(l==r)
{
scanf("%d",&sum[i]);
return;
}
int m=(l+r)/2;
build(lson);
build(rson);
sum[i]=sum[i<<1]+sum[i<<1|1];
}
void pushdown_eva(int i,int m)
{
eva[i<<1]=eva[i<<1|1]=eva[i];
sum[i<<1]=eva[i]*(m-m/2);
sum[i<<1|1]=eva[i]*(m/2);
eva[i]=0;
add[i<<1]=add[i<<1|1]=0; //赋值标记使得子区间的加值标记无效
}
void pushdown_add(int i,int m)
{
add[i<<1]+=add[i];
add[i<<1|1]+=add[i];
sum[i<<1]+=add[i]*(m-m/2);
sum[i<<1|1]+=add[i]*(m/2);
add[i]=0;
}
void update(int i,int l,int r,int q1,int q2,int num,int c)
{
if(q1<=l&&r<=q2)
{
if(c){
sum[i]=(r-l+1)*num;
eva[i]=num;
add[i]=0; //add标记被覆盖
}
else{
sum[i]+=(r-l+1)*num;
add[i]+=num;
}
return;
}
int m=(l+r)/2;
if(eva[i])
pushdown_eva(i,r-l+1);
if(add[i])
pushdown_add(i,r-l+1);
if(q1<=m)
update(lson,q1,q2,num,c);
if(q2>m)
update(rson,q1,q2,num,c);
sum[i]=sum[i<<1]+sum[i<<1|1];
}
int query(int i,int l,int r,int q1,int q2)
{
if(q1<=l&&r<=q2) return sum[i];
if(eva[i])
pushdown_eva(i,r-l+1);
if(add[i])
pushdown_add(i,r-l+1);
int m=(l+r)/2;
int t1=0,t2=0;
if(q1<=m)
t1=query(lson,q1,q2);
if(q2>m)
t2=query(rson,q1,q2);
return t1+t2;
}
int main()
{
// freopen("1.in","r",stdin);
int l,r,c,num;
scanf("%d%d",&n,&m);
{
n++;
build(1,1,n);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&c,&l,&r,&num);
l++;r++;
update(1,1,n,l,r,num,c);
printf("%d\n",query(1,1,n,1,n));
}
}
return 0;
}