【线段树专题】hdu4614

这个题的话思路不复杂

对于每个1操作,利用两次二分来查找它的操作区间,然后把这个区间都赋值为1,

对于每个2操作,直接把区间赋值为零好了。。

线段树维护一个sum存储当前区间为值0的点有多少个就好

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
struct rec{int lch,rch,sum,biao,l,r;}tree[200100];
int n,m,tot,ans_r,ans_l;
void updata(int x)
{
	if (tree[x].biao==0) return ;
	if (tree[x].biao==1)
	{
		if (tree[x].lch!=0)
		{
			int lc=tree[x].lch;
			tree[lc].sum=tree[lc].r-tree[lc].l+1;
			tree[lc].biao=tree[x].biao;
		}
		if (tree[x].rch!=0)
		{
			int rc=tree[x].rch;
			tree[rc].sum=tree[rc].r-tree[rc].l+1;
			tree[rc].biao=tree[x].biao;
		}
		tree[x].biao=0;
	}
	if (tree[x].biao==2)
	{
		if (tree[x].lch!=0)
		{
			int lc=tree[x].lch;
			tree[lc].sum=0;
			tree[lc].biao=tree[x].biao;
		}
		if (tree[x].rch!=0)
		{
			int rc=tree[x].rch;
			tree[rc].sum=0;
			tree[rc].biao=tree[x].biao;
		}
		tree[x].biao=0;
	}
}
void maketree(int l,int r)
{
	int now=++tot;
	tree[now].l=l;
	tree[now].r=r;
	if (l==r)
	{
		tree[now].sum=1;
		return ;
	}
	int mid=(l+r)>>1;
	tree[now].lch=tot+1;maketree(l,mid);
	tree[now].rch=tot+1;maketree(mid+1,r);
	tree[now].sum=tree[tree[now].lch].sum+tree[tree[now].rch].sum;
}
void change(int x,int l,int r,int w)
{
	updata(x);
	if (tree[x].l>=l&&tree[x].r<=r)
	{
		if (w==0) tree[x].sum=tree[x].r-tree[x].l+1;
		if (w==1) tree[x].sum=0;
		tree[x].biao=w+1;
		return ;
	}
	int mid=(tree[x].l+tree[x].r)>>1;
	if (l<=mid) change(tree[x].lch,l,r,w);
	if (r>mid) change(tree[x].rch,l,r,w);
	tree[x].sum=tree[tree[x].lch].sum+tree[tree[x].rch].sum;
}
int getsum(int x,int l,int r)
{
	int now=0;
	updata(x);
	if (tree[x].l>=l&&tree[x].r<=r)
		return tree[x].sum;
	int mid=(tree[x].l+tree[x].r)>>1;
	if (l<=mid) now+=getsum(tree[x].lch,l,r);
	if (r>mid) now+=getsum(tree[x].rch,l,r);
	return now;
}
int main()
{
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
	int a,b,c,ll,rr,t,tmp,mid;
	scanf("%d",&t);
	while (t--)
	{
		tot=0;
		memset(tree,0,sizeof(tree));
		scanf("%d%d",&n,&m);
		maketree(1,n);
		for (int i=1;i<=m;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			if (a==1)
			{
				b++;
				tmp=getsum(1,b,n);
				if (tmp==0) 
				{
					printf("Can not put any one.\n");
					continue;
				}
				tmp=min(tmp,c);
				ll=b,rr=n;
				while (ll!=rr)
				{
					mid=(ll+rr)>>1;
					if (getsum(1,b,mid)>=tmp) rr=mid;
					else ll=mid+1;
				}
				ans_r=rr;ans_l=ll;ll=b;rr=rr;
				while (ll!=rr)
				{
					mid=((ll+rr)>>1)+1;
					if (getsum(1,mid,ans_r)>=tmp) ll=mid;
					else rr=mid-1;
				}
				printf("%d %d\n",ll-1,ans_r-1);
				change(1,ll,ans_r,1);
			}
			if (a==2)
			{
                b++;c++;
				tmp=getsum(1,b,c);
				change(1,b,c,0);
				printf("%d\n",c-b+1-tmp);
			}
		}
		printf("\n");
	}
	return 0;
}

刚会学校的时候写这个东西还是有点费劲的,但是现在感觉已经是随手写了。果然是在进步吗,真是太好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值