acm专题9——线段树

代码解析

1.pushdown,pushup模板

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn=100000+10;
ll n,input[maxn];
struct node
{
	ll val,lazy,l,r,len;//lazy用来存储需要向下传的数值信息,等询问时再一起传递
}tree[4*maxn];
void build(ll root,ll l,ll r)
{
	ll m;
	tree[root].lazy=0;
	tree[root].l=l;
	tree[root].r=r;
	tree[root].len=r-l+1;
	if(l==r)
		tree[root].val=input[l];
	else
	{
		m=(l+r)/2;//二分建树
		build(root*2,l,m);//左子树
		build(root*2+1,m+1,r);//右子树
		tree[root].val=tree[root*2].val+tree[root*2+1].val;//pushup
	}
}
void pushdown(ll root)
{
	if(tree[root].lazy)
	{
		tree[root*2].lazy+=tree[root].lazy;//lazy标记传递
		tree[root*2+1].lazy+=tree[root].lazy;//lazy标记传递
		tree[root*2].val+=tree[root*2].len*tree[root].lazy;//lazy值传递该点的值
		tree[root*2+1].val+=tree[root*2+1].len*tree[root].lazy;//lazy值传递该点的值
		tree[root].lazy=0;//lazy标记清零
	}
}
void update(ll root,ll l,ll r,ll x)
{
	if(tree[root].l>=l&&tree[root].r<=r)//更新区间完全覆盖该子树区间
	{
		tree[root].lazy+=x;//更新lazy标记
		tree[root].val+=tree[root].len*x;//更新该点的值
		return;
	}
	if(tree[root].l>r||tree[root].r<l)
		return;
	pushdown(root);//若部分重叠,将lazy值传递下去,直到找到完全覆盖
	update(root*2,l,r,x);//更新左子树
	update(root*2+1,l,r,x);//更新右子树
	tree[root].val=(tree[root*2].val+tree[root*2+1].val);//pushup
}
ll query(ll root,ll l,ll r)
{
	if(tree[root].l>=l&&tree[root].r<=r)
		return tree[root].val;
	if(tree[root].l>r||tree[root].r<l)
		return 0;
	pushdown(root);
	return query(root*2,l,r)+query(root*2+1,l,r);
}
int main()
{
	while(~scanf("%lld",&n))
	{
		build(1,1,n);//建树
	}

    return 0;
}

2.标记永久化模板

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn=50000+10;
ll n,input[maxn];
struct node
{
	ll val,lazy,l,r,len;
}tree[4*maxn];
void build(ll root,ll l,ll r)//build部分不变
{
	ll m;
	tree[root].lazy=0;
	tree[root].l=l;
	tree[root].r=r;
	tree[root].len=r-l+1;
	if(l==r)
		tree[root].val=input[l];
	else
	{
		m=(l+r)/2;
		build(root*2,l,m);
		build(root*2+1,m+1,r);
		tree[root].val=tree[root*2].val+tree[root*2+1].val;
	}
}
void update(ll root,ll l,ll r,ll x)//更新每一个点!
{
	tree[root].val+=(r-l+1)*x;
	if(tree[root].l==l&&tree[root].r==r)//必须完全重合才可加lazy
	{
		tree[root].lazy+=x;
		return;
	}
	ll mid=(tree[root].l+tree[root].r)/2;//下标为root的结点的l与r的中点
	if(r<=mid)//如果更新区间的右边界比这个mid小,那么找这个root的左儿子,缩小区间
		update(root*2,l,r,x);
	else if(l>mid)//如果更新区间的左边界比这个mid大,那么找这个root的右儿子,缩小区间
		update(root*2+1,l,r,x);
	else
	{
		update(root*2,l,mid,x);
		update(root*2+1,mid+1,r,x);
	}
}
ll query(ll root,ll l,ll r,ll ad)//询问时累加标记,标记初始为0
{
	if(tree[root].l==l&&tree[root].r==r)
		return tree[root].val+tree[root].len*ad;
	ll mid=(tree[root].l+tree[root].r)/2;
	if(r<=mid) return query(root*2,l,r,ad+tree[root].lazy);
	if(l>mid) return query(root*2+1,l,r,ad+tree[root].lazy);
	return query(root*2,l,mid,ad+tree[root].lazy)+query(root*2+1,mid+1,r,ad+tree[root].lazy);
}
int main()
{
	while(~scanf("%lld",&n))
	{
		build(1,1,n);
	}
    return 0;
}

主要思想:分治
递归解决问题
优化方案:lazy标记
优化方案2:标记永久化

经典例题

1001-敌兵布阵

题目来源:hdoj(1166)

1.lazytag解法

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<long long,long long> pll;
const int inf = 0x3f3f3f3f;
const int maxn=100000+10;
ll n,input[maxn];
struct node
{
	ll val,lazy,l,r,len;//lazy用来存储需要向下传的数值信息,等询问时再一起传递
}tree[4*maxn];
void build(ll root,ll l,ll r)
{
	ll m;
	tree[root].lazy=0;
	tree[root].l=l;
	tree[root].r=r;
	tree[root].len=r-l+1;
	if(l==r)
		tree[root].val=input[l];
	else
	{
		m=(l+r)/2;//二分建树
		build(root*2,l,m);//左子树
		build(root*2+1,m+1,r);//右子树
		tree[root].val=tree[root*2].val+tree[root*2+1].val;//pushup
	}
}
void pushdown(ll root)
{
	if(tree[root].lazy)
	{
		tree[root*2].lazy+=tree[root].lazy;//lazy标记传递
		tree[root*2+1].lazy+=tree[root].lazy;//lazy标记传递
		tree[root*2].val+=tree[root*2].len*tree[root].lazy;//lazy值传递该点的值
		tree[root*2+1].val+=tree[root*2+1].len*tree[root].lazy;//lazy值传递该点的值
		tree[root].lazy=0;//lazy标记清零
	}
}
void update(ll root,ll l,ll r,ll x)
{
	if(tree[root].l>=l&&tree[root].r<=r)//更新区间完全覆盖该子树区间
	{
		tree[root].lazy+=x;//更新lazy标记
		tree[root].val+=tree[root].len*x;//更新该点的值
		return;
	}
	if(tree[root].l>r||tree[root].r<l)
		return;
	pushdown(root);//若部分重叠,将lazy值传递下去,直到找到完全覆盖
	update(root*2,l,r,x);//更新左子树
	update(root*2+1,l,r,x);//更新右子树
	tree[root].val=(tree[root*2].val+tree[root*2+1].val);//pushup
}
ll query(ll root,ll l,ll r)
{
	if(tree[root].l>=l&&tree[root].r<=r)
		return tree[root].val;
	if(tree[root].l>r||tree[root].r<l)
		return 0;
	pushdown(root);
	return query(root*2,l,r)+query(root*2+1,l,r);
}
void solve()
{
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++)
		scanf("%lld",&input[i]);
	build(1,1,n);//建树
	string s;
	ll a,b;
	while(cin>>s)
	{
		if(s=="End")
			break;
		cin>>a>>b;
		if(s=="Query")
			printf("%lld\n",query(1,a,b));
		else if(s=="Sub")
			update(1,a,a,-b);
		else if(s=="Add")
			update(1,a,a,b);
	}
    return;
}
int main()
{
	ll T;
	scanf("%lld",&T);
	for(ll t0=1;t0<=T;t0++)
	{
		printf("Case %lld:\n",t0);
		solve();
	}
	return 0;
}

2.标记永久化解法

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn=50000+10;
ll n,input[maxn];
struct node
{
	ll val,lazy,l,r,len;
}tree[4*maxn];
void build(ll root,ll l,ll r)
{
	ll m;
	tree[root].lazy=0;
	tree[root].l=l;
	tree[root].r=r;
	tree[root].len=r-l+1;
	if(l==r)
		tree[root].val=input[l];
	else
	{
		m=(l+r)/2;
		build(root*2,l,m);
		build(root*2+1,m+1,r);
		tree[root].val=tree[root*2].val+tree[root*2+1].val;
	}
}
void update(ll root,ll l,ll r,ll addval)
{
	tree[root].val+=(r-l+1)*addval;
	if(tree[root].l==l&&tree[root].r==r)
	{
		tree[root].lazy+=addval;
		return;
	}
	ll mid=(tree[root].l+tree[root].r)/2;
	if(r<=mid)
		update(root*2,l,r,addval);
	else if(l>mid)
		update(root*2+1,l,r,addval);
	else
	{
		update(root*2,l,mid,addval);
		update(root*2+1,mid+1,r,addval);
	}
}
ll query(ll root,ll l,ll r,ll ad)
{
	if(tree[root].l==l&&tree[root].r==r)
		return tree[root].val+tree[root].len*ad;
	ll mid=(tree[root].l+tree[root].r)/2;
	if(r<=mid) return query(root*2,l,r,ad+tree[root].lazy);
	if(l>mid) return query(root*2+1,l,r,ad+tree[root].lazy);
	return query(root*2,l,mid,ad+tree[root].lazy)+query(root*2+1,mid+1,r,ad+tree[root].lazy);
}
void solve()
{
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++)
		scanf("%lld",&input[i]);
	build(1,1,n);//建树
	string s;
	ll a,b;
	while(cin>>s)
	{
		if(s=="End")
			break;
		cin>>a>>b;
		if(s=="Query")
			printf("%lld\n",query(1,a,b,0));
		else if(s=="Sub")
			update(1,a,a,-b);
		else if(s=="Add")
			update(1,a,a,b);
	}
    return;
}
int main()
{
	ll T;
	scanf("%lld",&T);
	for(ll t0=1;t0<=T;t0++)
	{
		printf("Case %lld:\n",t0);
		solve();
	}
	return 0;
}

易错点:全部用cin读入会超时!!!还是尽量多用scanf为好

1002-I Hate It

题目来源:hdoj1754

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn=200000+10;
ll n,m,input[maxn];
struct node
{
	ll val,l,r;
}tree[4*maxn];
void build(ll root,ll l,ll r)//build部分不变
{
	ll m;
	tree[root].l=l;
	tree[root].r=r;
	if(l==r)
		tree[root].val=input[l];
	else
	{
		m=(l+r)/2;
		build(root*2,l,m);
		build(root*2+1,m+1,r);
		tree[root].val=max(tree[root*2].val,tree[root*2+1].val);
	}
}
void update(ll root,ll l,ll r,ll x)
{
	if(tree[root].l>=l&&tree[root].r<=r)
	{
		tree[root].val=x;
		return;
	}
	if(tree[root].l>r||tree[root].r<l)
		return;
	update(root*2,l,r,x);
	update(root*2+1,l,r,x);
	tree[root].val=max(tree[root*2].val,tree[root*2+1].val);
}
ll query(ll root,ll l,ll r)
{
	if(tree[root].l>=l&&tree[root].r<=r)
		return tree[root].val;
	if(tree[root].l>r||tree[root].r<l)
		return 0;
	return max(query(root*2,l,r),query(root*2+1,l,r));
}
void solve()
{
	build(1,1,n);
	while(m--)
	{
		char c;
		ll a,b;
		cin>>c>>a>>b;
		if(c=='Q')
			printf("%lld\n",query(1,a,b));
		else
			update(1,a,a,b);
	}
    return;
}
int main()
{
	while(~scanf("%lld%lld",&n,&m))
	{
		for(ll i=1;i<=n;i++)
			scanf("%lld",&input[i]);
		solve();
	}
	return 0;
}

1003-A Simple Problem with Integers

题目来源:hdoj4267

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn=50000+1;
int n,input[maxn];
struct node
{
	int val,l,r;
	int sum[57];
}tree[4*maxn];
void build(int root,int l,int r)//build部分不变
{
	int m;
	memset(tree[root].sum,0,sizeof(tree[root].sum));
	tree[root].l=l;
	tree[root].r=r;
	if(l==r)
		tree[root].val=input[l];
	else
	{
		m=(l+r)/2;
		build(root*2,l,m);
		build(root*2+1,m+1,r);
	}
}
void update(int root,int l,int r,int k,int x)
{
	if(tree[root].l>=l&&tree[root].r<=r)
	{
		tree[root].sum[k*(k-1)/2+(tree[root].l-l)%k]+=x;
		return;
	}
	if(tree[root].l>r||tree[root].r<l)
		return;
	update(root*2,l,r,k,x);
	update(root*2+1,l,r,k,x);
}
int query(int root,int x)
{
	if(tree[root].l>x||tree[root].r<x)
		return 0;
	int ans=0;
	for(int i=1;i<=10;i++)
        for(int j=0;j<i;j++)
            if((x-tree[root].l+j)%i==0)
				ans+=tree[root].sum[i*(i-1)/2+j];
	if(tree[root].l==tree[root].r)
		return ans+=tree[root].val;
	int m=(tree[root].l+tree[root].r)/2;
	return ans+=query(root*2,x)+query(root*2+1,x);
}
void solve()
{
	build(1,1,n);
	int m,op,a,b,c,d;
	scanf("%d",&m);
	while(m--)
	{
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d%d%d%d",&a,&b,&c,&d);
			update(1,a,b,c,d);
		}
		else
		{
			scanf("%d",&a);
			printf("%d\n",query(1,a));
		}
	}
    return;
}
int main()
{
	while(~scanf("%d",&n))
	{
		for(int i=1;i<=n;i++)
			scanf("%d",&input[i]);
		solve();
	}
	return 0;
}

这题让我推开了新世界的大门
首先先说一下坑点,如果你是一位longlongint选手,那么恭喜你,你很有可能会像我一样,memory limited exceeded。请不要没事define int long long int
对这个sum做一点解释:
k*(k-1)/2就是0到k-1的和
比方说k=1的时候,这个和就是0,余数必然为0,存储一种情况(sum[0])
k=2的时候,这个和就是1,此时余数可以为0或为1,存储两种情况(sum[1+0],sum[1+1])
k=3的时候,这个和就是3,此时余数可以为0或为1或为2,存储两种情况(sum[3+0],sum[3+1],sum[3+2])

1004-Just a Hook

题目来源:hdoj1698

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<long long,long long> pll;
const int inf = 0x3f3f3f3f;
const int maxn=100000+10;
ll n,input[maxn];
struct node
{
	ll val,lazy,l,r,len;
}tree[4*maxn];
void build(ll root,ll l,ll r)
{
	ll m;
	tree[root].lazy=0;
	tree[root].l=l;
	tree[root].r=r;
	tree[root].len=r-l+1;
	if(l==r)
		tree[root].val=1;
	else
	{
		m=(l+r)/2;
		build(root*2,l,m);
		build(root*2+1,m+1,r);
		tree[root].val=tree[root*2].val+tree[root*2+1].val;
	}
}
void pushdown(ll root)
{
	if(tree[root].lazy)
	{
		tree[root*2].lazy=tree[root].lazy;
		tree[root*2+1].lazy=tree[root].lazy;
		tree[root*2].val=tree[root*2].len*tree[root].lazy;
		tree[root*2+1].val=tree[root*2+1].len*tree[root].lazy;
		tree[root].lazy=0;
	}
}
void update(ll root,ll l,ll r,ll x)
{
	if(tree[root].l>=l&&tree[root].r<=r)
	{
		tree[root].lazy=x;
		tree[root].val=tree[root].len*x;
		return;
	}
	if(tree[root].l>r||tree[root].r<l)
		return;
	pushdown(root);
	update(root*2,l,r,x);
	update(root*2+1,l,r,x);
	tree[root].val=(tree[root*2].val+tree[root*2+1].val);
}
ll query(ll root,ll l,ll r)
{
	if(tree[root].l>=l&&tree[root].r<=r)
		return tree[root].val;
	if(tree[root].l>r||tree[root].r<l)
		return 0;
	pushdown(root);
	return query(root*2,l,r)+query(root*2+1,l,r);
}
void solve()
{
	ll m;
	ll a,b,c;
	scanf("%lld%lld",&n,&m);
	build(1,1,n);
	while(m--)
	{
		scanf("%lld%lld%lld",&a,&b,&c);
		update(1,a,b,c);
	}
	printf("%lld.\n",query(1,1,n));
    return;
}
int main()
{
	ll T;
	scanf("%lld",&T);
	for(ll i=1;i<=T;i++)
	{
		printf("Case %lld: The total value of the hook is ",i);
		solve();
	}
	return 0;
}

模板题

1005-LCIS

题目来源:hdoj3308

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10000;
int n,m,a,b,t,ans;
int x[maxn];
char op[2];

struct node{
    int l,r; //左右边界
    int ls,rs; //左右边界的值
    int lsum,rsum,sum; //左右最大LCIS 区间最大LCIS
}tree[maxn*4];

void pushup(int rt)
{
    tree[rt].ls=tree[rt*2].ls;
    tree[rt].rs=tree[rt*2+1].rs;
    tree[rt].lsum=tree[rt*2].lsum;
    tree[rt].rsum=tree[rt*2+1].rsum;
    tree[rt].sum=max(tree[rt*2].sum,tree[rt*2+1].sum);
    if(tree[rt*2].rs<tree[rt*2+1].ls)
	{//如果左子树的右边界值小于右子树的左边界值 合并左子树的右边界和右子树的左边界进行计算
        if(tree[rt*2].lsum==(tree[rt*2].r-tree[rt*2].l+1))
            tree[rt].lsum+=tree[rt*2+1].lsum;
        if(tree[rt*2+1].rsum==(tree[rt*2+1].r-tree[rt*2+1].l+1))
            tree[rt].rsum+=tree[rt*2].rsum;
        tree[rt].sum=max(tree[rt].sum,tree[rt*2].rsum+tree[rt*2+1].lsum);
    }
}

void build(int l,int r,int rt)
{
    tree[rt].l=l;
    tree[rt].r=r;
    if(l==r)
	{
        tree[rt].lsum=tree[rt].rsum=tree[rt].sum=1;
        tree[rt].ls=tree[rt].rs=x[l];
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,rt*2);
    build(mid+1,r,rt*2+1);
    pushup(rt);
}

void update(int rt)
{
    if(tree[rt].l==tree[rt].r)
	{
        tree[rt].ls=tree[rt].rs=b;
        return;
    }
    int mid=(tree[rt].l+tree[rt].r)/2;
    if(a<=mid) update(rt*2);
    else update(rt*2+1);
    pushup(rt);
}

int query(int rt)
{
    if(tree[rt].l>=a && tree[rt].r<=b)
        return tree[rt].sum;
    int mid=(tree[rt].l+tree[rt].r)/2;
    int ans=0;
    if(a<=mid) ans=max(ans,query(rt*2));
    if(b>mid) ans=max(ans,query(rt*2+1));
    if(tree[rt*2].rs<tree[rt*2+1].ls)
        ans=max(ans,min(mid-a+1,tree[rt*2].rsum)+min(b-mid,tree[rt*2+1].lsum));
    return ans;
}

int main()
{
    scanf("%d",&t);
    while(t--)
	{
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&x[i]);
        build(1,n,1);
        for(int i=1;i<=m;i++)
		{
            scanf("%s%d%d",op,&a,&b);
            if(op[0]=='U')
			{
                a++;
                update(1);
            }
            if(op[0]=='Q')
			{
                a++;b++;
                printf("%d\n",query(1));
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值