poj线段树专题

 

 

poj 2528 水题

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 40500
int n;
struct node
{
	int l,r,flag;
}tree[N*4];
struct Inter
{
	int l,r;
}inter[N];
int vis[N];
int max(int aa,int bb)
{
	return aa>bb?aa:bb;
}
void build(int pos,int l,int r)
{
	tree[pos].l=l;tree[pos].r=r;
	tree[pos].flag=0;
	if(l==r)
		return;
	int mid=(l+r)>>1;
	build(pos*2,l,mid);
	build(pos*2+1,mid+1,r);
}
void update(int pos,int l,int r,int val)
{
	if(l<=tree[pos].l&&tree[pos].r<=r)
	{
		tree[pos].flag=val;
		return;
	}
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(r<=mid)
		update(pos*2,l,r,val);
	else if(l>mid)
		update(pos*2+1,l,r,val);
	else
	{
		update(pos*2,l,mid,val);
		update(pos*2+1,mid+1,r,val);
	}
}
int query(int pos,int x)
{
	if(tree[pos].l==tree[pos].r)
		return tree[pos].flag;
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(x<=mid)
		return max(tree[pos].flag,query(pos*2,x));
	else if(x>mid)
		return max(tree[pos].flag,query(pos*2+1,x));
}
struct point
{
	int idx,val;
}tt[N*2];
bool cmp(point left,point right)
{
	return left.val<right.val;
}
int main ()
{
	int test;scanf("%d",&test);
	while(test--)
	{
		memset(vis,0,sizeof(vis));
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
		{
			scanf("%d%d",&inter[i].l,&inter[i].r);
			tt[2*i-1].val=inter[i].l;tt[2*i-1].idx=i;
			tt[2*i].val=inter[i].r;tt[2*i].idx=i;
		}
		sort(tt+1,tt+1+2*n,cmp);
		int pre=tt[1].val-1;int cnt=0;
		for(int i=1;i<=2*n;++i)
		{
			if(pre!=tt[i].val)
			{
				pre=tt[i].val;
				tt[i].val=++cnt;
			}
			else tt[i].val=cnt;
			if(pre==inter[tt[i].idx].l)
				inter[tt[i].idx].l=cnt;
			if(pre==inter[tt[i].idx].r)
				inter[tt[i].idx].r=cnt;
		}
		build(1,1,cnt);
		for(int i=1;i<=n;++i)
			update(1,inter[i].l,inter[i].r,i);
		int temp;
		int ans=0;vis[0]=1;
		for(int i=1;i<=cnt;++i)
		{
			temp=query(1,i);
			if(!vis[temp])
				ans++;
			vis[temp]=1;
		}
		printf("%d\n",ans);
	}

	return 0;
}


poj 2828

一开始还天真的用vector模拟,事实证明vector真是慢,insert不是o(1)的

此题成功之处是看不出事线段树

一个人排在什么位置和插入的位置和顺序有关,在插入的过程中是动态变化的

所以从最后排的人扫回去,他排在pos后面,相当于询问某个位置前面有多少个空位
 

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 250000
int n;
struct node
{
	int l,r,val;
}tree[N*4];
struct point
{
	int pos,val;
}p[N];
void build(int pos,int l,int r)
{
	tree[pos].l=l;tree[pos].r=r;
	tree[pos].val=0;
	if(l==r)
		return ;
	int mid=(l+r)>>1;
	build(pos*2,l,mid);
	build(pos*2+1,mid+1,r);
}
void update(int pos,int x,int val)
{
	if(tree[pos].l==tree[pos].r)
	{
		tree[pos].val++;
		return;
	}
	tree[pos].val++;
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(x<=mid)
		update(pos*2,x,val);
	else update(pos*2+1,x,val);
}
int query(int pos,int l,int r)
{
	if(l<=tree[pos].l&&tree[pos].r<=r)
		return tree[pos].val;
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(r<=mid)
		return query(pos*2,l,r);
	else if(l>mid)
		return query(pos*2+1,l,r);
	else return query(pos*2,l,mid)+query(pos*2+1,mid+1,r);
}
int ans[N];
int main ()
{
	while(scanf("%d",&n)!=EOF)
	{
		build(1,1,n);
		for(int i=1;i<=n;++i)
			scanf("%d%d",&p[i].pos,&p[i].val);
		int l,r,mid,temp;
		for(int i=n;i>=1;--i)
		{
			l=1;r=n;
			while(l<r)
			{
				mid=(l+r)>>1;
				temp=query(1,1,mid);
				if(mid-temp<p[i].pos+1)
					l=mid+1;
				else r=mid;
			}
			ans[l]=p[i].val;
			update(1,l,1);
		}
		for(int i=1;i<=n;++i)
		{
			printf("%d",ans[i]);
			if(i<n)
				printf(" ");
			else printf("\n");
		}
	}
	return 0;
}

 

 

poj 2777

1.关于lazy的优化:当刚好能够覆盖节点表示的区间,就不再往下走,并做标记;对于途中经过的点,先把节点的状态push down 给子节点再往下走,

子节点update完成后,再push up 把子节点的状态反馈给自己。询问时遇到有标记被整个区间覆盖的节点就不再往下走。

这能够避免每次更新或者询问总是几乎遍历整棵树

2.用一个32位整数表示状态,每个节点储存包含哪些颜色

 

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 105000
int L,T,O;
struct node
{
	int l,r,flag;
	int color;
}tree[N*4];

void build(int pos,int l,int r)
{
	tree[pos].l=l;tree[pos].r=r;
	tree[pos].flag=1;tree[pos].color=1;
	if(l==r)
		return;
	int mid=(l+r)>>1;
	build(pos*2,l,mid);
	build(pos*2+1,mid+1,r);
}
void update(int pos,int l,int r,int val)
{
	if(l<=tree[pos].l&&tree[pos].r<=r)
	{
		tree[pos].flag=1;
		tree[pos].color=(1<<(val-1));
		return;
	}
	if(tree[pos].flag)  // push down
	{
		tree[pos*2].color=tree[pos].color;
		tree[pos*2+1].color=tree[pos].color;
		tree[pos*2].flag=tree[pos*2+1].flag=1;
		tree[pos].flag=0;
	}
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(r<=mid)
		update(pos*2,l,r,val);
	else if(l>mid)
		update(pos*2+1,l,r,val);
	else
	{
		update(pos*2,l,mid,val);
		update(pos*2+1,mid+1,r,val);
	}
	tree[pos].flag=0;
	tree[pos].color=tree[pos*2].color|tree[pos*2+1].color;  // push up
}
int query(int pos,int l,int r)
{
	if(l<=tree[pos].l&&tree[pos].r<=r||tree[pos].flag)
		return tree[pos].color;
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(r<=mid)
		return query(pos*2,l,r);
	else if(l>mid)
		return query(pos*2+1,l,r);
	else return query(pos*2,l,mid)|query(pos*2+1,mid+1,r);
}
int decode(int x)
{
	int ans=0;
	while(x)
	{
		if(x&1)
			ans++;
		x>>=1;
	}
	return ans;
}
int main ()
{
	scanf("%d%d%d",&L,&T,&O);
		char ch;int a,b,c;
		build(1,1,L);
		while(O--)
		{
			scanf(" %c",&ch);
			if(ch=='P')
			{
				scanf("%d%d",&a,&b);
				if(b<a)
					swap(a,b);
				int t=query(1,a,b);
				int ans=decode(t);
				printf("%d\n",ans);
			}
			else
			{
				scanf("%d%d%d",&a,&b,&c);
				if(b<a)
					swap(a,b);
				update(1,a,b,c);
			}
		}
	return 0;
}


 

 poj 2886

线段树的另一功能:获得一个未出列的人中的排位对应的下标!!

应用反素数,先把表打出来,不要放进里面算

之前以为用欧拉函数,把不与n互质的数的个数 和 小于n的数中能整除n的数的个数 混淆

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 550500
int phi[N+49],n,k;
int card[N];
char str[N][15];
int RPrime[]={//反素数  
    1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,  
    20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960,  
    554400  
};  
  
int fact[]={//反素数约数个数  
    1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,84,90,96,100,108,120,128,  
    144,160,168,180,192,200,216  
};  
struct node
{
	int l,r,sum;
}tree[N*4];
void build(int pos,int l,int r)
{
	tree[pos].l=l;tree[pos].r=r;
	if(l==r)
	{
		tree[pos].sum=1;
		return;
	}
	int mid=(l+r)>>1;
	build(pos*2,l,mid);
	build(pos*2+1,mid+1,r);
	tree[pos].sum=tree[pos*2].sum+tree[pos*2+1].sum;
}
int luck;
void update(int pos,int idx)
{
	tree[pos].sum--;
	if(tree[pos].l==tree[pos].r)
	{
		luck=tree[pos].l;
		return;
	}
	if(idx>tree[pos*2].sum)
		update(pos*2+1,idx-tree[pos*2].sum);
	else update(pos*2,idx);
}


int main ()
{
	while(scanf("%d%d",&n,&k)!=EOF)
	{
		for(int i=1;i<=n;++i)
			scanf("%s%d",str[i],&card[i]);
		int ma=0,high=fact[ma];
		for(int i=0;i<=36&&RPrime[i]<=n;++i)
			if(fact[ma]<fact[i])
				ma=i,high=RPrime[i];
		build(1,1,n);
		int r=1;luck=0;
		for(int i=1;i<=high;++i)
		{
			int mod=tree[1].sum;
			if(k>0)
				r=(r-2+k%mod+mod)%mod+1;
			else r=(r-1+k%mod+mod)%mod+1;
			update(1,r);
			k=card[luck];
			//printf("%s\n",str[luck]);
			//printf("k=%d  r=%d   luck=%d\n",k,r,luck);
		}
		printf("%s %d\n",str[luck],fact[ma]);
	}
	return 0;
}


 

 poj 2750

经典的单点更新,求区间最值,没有想到怎么lazy

现在想想倒不用lazy,lazy是区间更新时避免更新很多个单点而变成超时的,现在是单点,无压力

tree[pos].max=max(tree[pos*2].max,tree[pos*2+1].max,tree[pos*2].rmax+tree[pos*2+1].lmax)

接下来就是3个问题:

1.环:转化为求连续子区间的最大值或者总和减去连续子区间的最小值

2.答案的区间不能使整个区间,只要在更新时加一句判断就可以了

3.特判:所有数都是负的的特殊情况

 

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define N 105000
struct node
{
	int l,r,lmax,lmin,rmax,rmin,max,min,sum,flag;
}tree[N*4];
int a[N*2],n,m;
inline int max(int aa,int bb)
{
	return aa>bb?aa:bb;
}
inline int min(int aa,int bb)
{
	return aa<bb?aa:bb;
}
void push_up(int pos)
{
	tree[pos].flag=(tree[pos*2].flag&&tree[pos*2+1].flag);
	tree[pos].sum=tree[pos*2].sum+tree[pos*2+1].sum;
	tree[pos].lmax=max(tree[pos*2].lmax,tree[pos*2].sum+tree[pos*2+1].lmax);
	tree[pos].rmax=max(tree[pos*2+1].rmax,tree[pos*2+1].sum+tree[pos*2].rmax);
	tree[pos].max=max(tree[pos*2].max,tree[pos*2+1].max);
	if(tree[pos*2].rmax+tree[pos*2+1].lmax!=tree[1].sum)
		tree[pos].max=max(tree[pos].max,tree[pos*2].rmax+tree[pos*2+1].lmax);
	tree[pos].lmin=min(tree[pos*2].lmin,tree[pos*2].sum+tree[pos*2+1].lmin);
	tree[pos].rmin=min(tree[pos*2+1].rmin,tree[pos*2+1].sum+tree[pos*2].rmin);
	tree[pos].min=min(min(tree[pos*2].min,tree[pos*2+1].min),tree[pos*2].rmin+tree[pos*2+1].lmin);
}
void build(int pos,int l,int r)
{
	tree[pos].l=l;tree[pos].r=r;
	if(l==r)
	{
		tree[pos].max=tree[pos].min=tree[pos].sum=tree[pos].lmax=tree[pos].lmin=tree[pos].rmax=tree[pos].rmin=a[l];
		if(a[l]<0) tree[pos].flag=1;
		return;
	}
	int mid=(l+r)>>1;
	build(pos*2,l,mid);
	build(pos*2+1,mid+1,r);
	push_up(pos);
}
void update(int pos,int idx,int val)
{
	if(tree[pos].l==tree[pos].r)
	{
		tree[pos].lmax=tree[pos].rmax=tree[pos].lmin=tree[pos].rmin=tree[pos].sum=val;
		tree[pos].max=tree[pos].min=val;
		if(val<0) tree[pos].flag=1;
		return;
	}
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(idx<=mid)
		update(pos*2,idx,val);
	else update(pos*2+1,idx,val);
	push_up(pos);
}
int main ()
{
	while(scanf("%d",&n)!=EOF)
	{
		for(int i=1;i<=n;++i)
			scanf("%d",&a[i]);
		build(1,1,n);
		scanf("%d",&m);
		int A,B;
		int res;
		while(m--)
		{
			scanf("%d%d",&A,&B);
			update(1,A,B);
			res=tree[1].max;
			if(!tree[1].flag)
				res=max(res,tree[1].sum-tree[1].min);
			printf("%d\n",res);
		}
	}
	return 0;
}


 

poj 2482

1.二维线段树,二维限制,先排序,边插入便询问

2.离散化

3 把询问当做点放进里面排序

(gmj总结的常考点基本在这题都出来了)

这里有个trick,左下角的点权值为c,右上角的权值设为-c,这样在超过矩形的范围后由于区间加上-c,c的影响被消除,所以这时询问不到c的影响

关于线段树的区间更新,区间询问之前一直不太理解:

每个节点记录已加值(加在节点表示的整个区间)和待加值(加在节点表示区间的部分区间)

每次更新往下跑,直到目标区间与节点表示区间一样,加在已加值上,表示对整段的更新

注意点在矩形的左下角或者右上角的时候不一定是答案,所以开始我用树状数组水不过= =,比如

2 3 3 

3 1 1

1 3 1

就过不了

其他的就是考验边界的处理了



#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define N 35500
#define ll long long
int n,w,h;
struct node
{
	ll x,y;
	int flag,c,id;
}p[N];
struct Inter
{
	int x0,y0,x1,y1;
}inter[N];
int ly[N],lx[N];
struct pnode
{
	int l,r,val,max;
}tree[N*4];
int max(int aa,int bb)
{
	return aa>bb?aa:bb;
}
void build(int pos,int l,int r)
{
	tree[pos].l=l;tree[pos].r=r;
	tree[pos].val=tree[pos].max=0;
	if(l==r)
		return;
	int mid=(l+r)>>1;
	build(pos*2,l,mid);
	build(pos*2+1,mid+1,r);
}
void update(int pos,int l,int r,int v)
{
	if(l<=tree[pos].l&&tree[pos].r<=r)  // 不再往下跑,表示整段更新
	{
		tree[pos].max+=v;
		tree[pos].val+=v;
		return;
	}
	//tree[pos].idx+=v;  加在idx上的区间可能不相交,错!
	int mid=(tree[pos].l+tree[pos].r)>>1;
	if(r<=mid)
		update(pos*2,l,r,v);
	else if(l>mid)
		update(pos*2+1,l,r,v);
	else
	{
		update(pos*2,l,mid,v);
		update(pos*2+1,mid+1,r,v);
	}
	tree[pos].max=max(tree[pos*2].max,tree[pos*2+1].max)+tree[pos].val;
}

bool cmpx(int left,int right)
{
	return p[left].x<p[right].x;
}
bool cmpy(int left,int right)
{
	return p[left].y<p[right].y;
}
bool cmp(node left,node right)
{
	if(left.x==right.x)
	{
		if(left.y==right.y)
			return left.flag>right.flag; // 先插入再询问
		return left.y<right.y;
	}
	return left.x<right.x;
}
int main ()
{
	while(scanf("%d%d%d",&n,&w,&h)!=EOF)
	{
		for(int i=1;i<=n;++i)
		{
			scanf("%lld%lld%d",&p[2*i-1].x,&p[2*i-1].y,&p[2*i-1].c);
			p[2*i-1].flag=1;p[2*i-1].id=i;
			p[2*i].x=p[2*i-1].x+w-1;
			p[2*i].y=p[2*i-1].y+h-1;
			p[2*i].c=-p[2*i-1].c;
			p[2*i].flag=0;p[2*i].id=i;
		}
		sort(p+1,p+1+2*n,cmp);
		for(int i=1;i<=2*n;++i)
			lx[i]=ly[i]=i;
		sort(lx+1,lx+1+2*n,cmpx);
		sort(ly+1,ly+1+2*n,cmpy);
		ll pre=p[lx[1]].x-2;
		int cnt=1;
		for(int i=1;i<=2*n;++i)
			if(pre!=p[lx[i]].x)
				pre=p[lx[i]].x,p[lx[i]].x=++cnt;
			else p[lx[i]].x=cnt;
		pre=p[ly[1]].y-2;
		cnt=1;
		for(int i=1;i<=2*n;++i)
			if(pre!=p[ly[i]].y)
				pre=p[ly[i]].y,p[ly[i]].y=++cnt;
			else p[ly[i]].y=cnt;
		for(int i=1;i<=2*n;++i)
			if(p[i].flag)
				inter[p[i].id].x0=p[i].x,inter[p[i].id].y0=p[i].y;
			else inter[p[i].id].x1=p[i].x,inter[p[i].id].y1=p[i].y;

		
		build(1,1,p[ly[2*n]].y);
		int ans=-1;
		for(int i=1;i<=2*n;++i)
		{
			if(ans<tree[1].max)
				ans=tree[1].max;
			update(1,inter[p[i].id].y0,inter[p[i].id].y1,p[i].c);
		}
		printf("%d\n",ans);
	}
	return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值