Pein线段树总结(陆续更新ing)

这几天对着notonlysuccess的完全版线段树,因为登不了国外的OJ,所以只跟着刷了POJ和HDOJ,之后有好的线段树题目会陆续更新上来!

简单介绍下我的代码风格:

                一:maxn题目所给区间,节点数maxn<<2.

                二:lson,rson表示左儿子和右儿子

                       我喜欢宏定义#define lson id<<1,l,mid

                                               #define rson id<<1|1,mid+1,r

                               得注意下如果节点存的不是点而是线段的时候,得#define rson id<<1|1,mid,r

                三:update函数:更新操作

                四:PushDown函数,懒惰标记下传给儿子,在成端更新的时候为了效率我们只更新到指定段,并不更新到

                                 叶子节点,而当下一次操作询问到该段的某一部分,或者是对该段的某一部分进行更新操作的时候,

                                 就需要将之前的懒惰标记向下传递

                五:PushUp函数,当执行完更新操作后,将儿子节点的信息更新到父节点,这样父节点的权值一直是正确

                        的,询问指定区间的时候,只要返回该区间对应的权值

线段树的题型主要分为四类:单点更新   成段更新   区间合并   扫描线

一:单点更新

HDU1166敌兵布阵

题意:单点更改,询问区间和

思路:只需要update函数单点更改,PushUp函数维护区间和

#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 50080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int A[maxn];
char ope[10];
struct ST
{
	int l,r,key;
}st[maxn<<2];
void PushUp(int id)
{
	st[id].key = st[id<<1].key + st[id<<1|1].key;
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r;
	if(l == r)
	{
		st[id].key = A[l];
		return;
	}
	int mid = (l + r)>>1;
	buildtree(lson);
	buildtree(rson);
	PushUp(id);
}
void update(int id,int pos,int add)
{
	if(st[id].l == pos && st[id].r == pos)
	{
		st[id].key += add;
		return;
	}
	if(st[id<<1].r >= pos)		
		update(id<<1,pos,add);
	else update(id<<1|1,pos,add);
	PushUp(id);
}
int query(int id,int l,int r)
{
	if(st[id].l == l && st[id].r == r)	return st[id].key;
	if(st[id<<1].r >= r)	return query(id<<1,l,r);
	if(st[id<<1|1].l <= l)		return query(id<<1|1,l,r);
	return query(id<<1,l,st[id<<1].r) + query(id<<1|1,st[id<<1|1].l,r);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int t;	scanf("%d",&t);
	for(int cas = 1;cas <= t;cas++)
	{
		int n;	scanf("%d",&n);
		for(int i = 1;i <= n;i++)	scanf("%d",&A[i]);
		buildtree(1,1,n);
		printf("Case %d:\n",cas);
		while(scanf("%s",ope)!=EOF && ope[0]!='E')
		{
			int u,v;	scanf("%d%d",&u,&v);
			if(ope[0] == 'Q')	printf("%d\n",query(1,u,v));
			if(ope[0] == 'A')	update(1,u,v);
			if(ope[0] == 'S')	update(1,u,-v);
		}
	}
	return 0;
}


 

HDU1754 I Hate It

题意:单点更改,询问区间最大值

思路:update函数单点更改,PushUp函数维护区间最大值

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 200080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int A[maxn];
struct ST
{
    int l,r,key;
}st[maxn<<2];
inline int max(int a,int b)
{
    return a>b?a:b;
}
void PushUp(int id)
{
    st[id].key = max(st[id<<1].key,st[id<<1|1].key);
}
void buildtree(int id,int l,int r)
{
    st[id].l = l,st[id].r = r;
    if(l == r)
    {
        st[id].key = A[l];
        return;
    }
    int mid = (l + r)>>1;
    buildtree(lson);
    buildtree(rson);
    PushUp(id);
}
void update(int id,int pos,int newk)
{
    if(st[id].l == pos && st[id].r == pos)
    {
        st[id].key = newk;
        return;
    }
    if(st[id<<1].r >= pos)    
        update(id<<1,pos,newk);
    else    update(id<<1|1,pos,newk);
    PushUp(id);
}
int query(int id,int l,int r)
{
    if(st[id].l == l && st[id].r == r)    return st[id].key;
    if(st[id<<1].r >= r)    return query(id<<1,l,r);
    if(st[id<<1|1].l <= l)    return query(id<<1|1,l,r);
    return max(query(id<<1,l,st[id<<1].r),query(id<<1|1,st[id<<1|1].l,r));
}
int main()
{
    //freopen("in.txt","r",stdin);
    int n,m;
    while(scanf("%d%d",&n,&m)==2)
    {
        for(int i = 1;i <= n;i++)    scanf("%d",&A[i]);
        char ope[2];int u,v;
        buildtree(1,1,n);
        for(int i = 1;i <= m;i++)
        {
            scanf("%s%d%d",ope,&u,&v);
            if(ope[0] == 'Q')    printf("%d\n",query(1,u,v));
            else update(1,u,v);
        }
    }
    return 0;
}

 

HDU1394 Minimum Inversion Number

题意:给一行数,每次可将第一个移到最后一个,求所有情况中的最小逆序数

思路:节点表示区间[l,r]出现了多少个数.每读入一个数,query一下即可得到该数的逆序对,然后update.求出逆序然后就是简单递推

PS:归并排序求逆序数的方法必须掌握,用线段树的话如果数太大是行不通的,可切下POJ2299就知道

            还有这里数是0到n.要么建树buildtree(1,0,n-1),要么将A[i]集体++.

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 5080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int A[maxn];
struct ST
{
	int l,r,key;
}st[maxn<<2];
inline int min(int a,int b)
{
	return a>b?b:a;
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].key = 0;
	if(l == r)	return;
	int mid = (l + r)>>1;
	buildtree(lson);
	buildtree(rson);
}
void PushUp(int id)
{
	st[id].key = st[id<<1].key + st[id<<1|1].key;
}
void update(int id,int pos)
{
	if(st[id].l == pos && st[id].r == pos)
	{
		st[id].key = 1;	
		return;
	}
	if(st[id<<1].r >= pos)
		update(id<<1,pos);
	else update(id<<1|1,pos);
	PushUp(id);
}
int query(int id,int l,int r)
{
	if(st[id].l == l && st[id].r == r)	return st[id].key;
	if(st[id<<1].r >= r)	return query(id<<1,l,r);
	if(st[id<<1|1].l <= l)	return query(id<<1|1,l,r);
	return query(id<<1,l,st[id<<1].r) + query(id<<1|1,st[id<<1|1].l,r);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int n;
	while(scanf("%d",&n)!=EOF && n)
	{
		int sum = 0;
		buildtree(1,1,n);
		for(int i = 1;i <= n;i++)
		{
			scanf("%d",&A[i]);	A[i]++;
			update(1,A[i]);
			if(A[i] < n)	sum += query(1,A[i]+1,n);
		}
		//sum就是初始逆序数
		int now = sum,ans = sum;
		for(int i = 1;i < n;i++)
		{
			if(A[i] < n)	now += 2*query(1,A[i]+1,n) - n + 1;
			else now -= n-1;
			ans = min(ans,now);
		}
		printf("%d\n",ans);
	}
	return 0;
}

HDU2795 Billboard

题意:往黑板贴海报,1.尽量往上2.尽量靠左

思路:节点存区间最大值,update函数单点更改后,PushUp函数向上传递维护区间最大值

PS:建树的时候区间上限只需要min(h,n).

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 200080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int w,h,pos;
inline int max(int a,int b)
{
	return a>b?a:b;
}
inline int min(int a,int b)
{
	return a>b?b:a;
}
struct ST
{
	int l,r,key;
}st[maxn<<2];
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].key = w;
	if(l == r)	return;
	int mid = (l + r)>>1;
	buildtree(lson);
	buildtree(rson);
}
void PushUp(int id)
{
	st[id].key = max(st[id<<1].key,st[id<<1|1].key);
}
void update(int id,int len)
{
	if(st[id].l == st[id].r)
	{
		pos = st[id].l;
		st[id].key -= len;
		return;
	}
	if(st[id<<1].key >= len)
		update(id<<1,len);
	else if(st[id<<1|1].key >= len)
		update(id<<1|1,len);
	PushUp(id);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int n;
	while(scanf("%d%d%d",&h,&w,&n)==3)
	{
		buildtree(1,1,min(n,h));
		for(int i = 1;i <= n;i++)
		{
			int a;pos = -1;
			scanf("%d",&a);
			if(st[1].key >= a)
			update(1,a);	printf("%d\n",pos);
		}
	}
	return 0;
}
		
	

POJ2828 Buy Tickets

题意:N个人,每个人找个地方插队。求最终队形

思路:挺巧的一道题,先读入操作,然后反向操作,一个人要插在i之后,则他前面必须有i个空位。非空位说明比他晚来的人有人插在他前面嘛。update函数即是占位,PushUp函数维护区间剩余空位数

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 200080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int u[maxn],v[maxn],POS,ans[maxn];
struct ST
{
	int l,r,res;
}st[maxn<<2];
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].res = r - l +1;
	if(l == r)	return;
	int mid = (l+r)>>1;
	buildtree(lson);
	buildtree(rson);
}
void PushUp(int id)
{
	st[id].res = st[id<<1].res + st[id<<1|1].res;
}
void update(int id,int pos)
{
	if(st[id].l == st[id].r)
	{
		st[id].res = 0;	
		POS = st[id].l;
		return;
	}
	if(st[id<<1].res >= pos)
		update(id<<1,pos);
	else update(id<<1|1,pos-st[id<<1].res);
	PushUp(id);
}
	
int main()
{
	//freopen("in.txt","r",stdin);
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		buildtree(1,1,n);
		for(int i = 1;i <= n;i++)
		{
			scanf("%d%d",&u[i],&v[i]);
		}
		for(int i = n;i >= 1;i--)
		{
			update(1,u[i]+1);
			ans[POS] = v[i];
		}
		for(int i = 1;i <= n;i++)
		{
			printf("%d",ans[i]);
			if(i == n)	printf("\n");
			else printf(" ");
		}
	}
	return 0;
}

POJ2886 Who Gets the Most Candies?

题意:N个小伙伴围在一起,每个小伙伴手中有张卡片,意味着下一个出来的是他左边或右边第几个,求第K个出来的是谁。K即是N内对应因子数最多的那个。

思路:预处理求N内对应的K,将环看成直线,每一次用取余的方法得到要T的是第几个人,再update.

 

/*
反素数的求法。
搜百度搜了好久都找不到细讲的,最后既然搜到队长的博客O(∩_∩)O哈哈~
显然素数的因子数为2;
至于非素数,可以分解为质因子之积
i=2^b1*3^b2^5^b3------等等
所以i的因子个数=(b1+1)*(b2+1)*(b3+1)~~~*(bn+1)
反素数的求法:筛选法+DP;素数的约数个数显然是2,对于合数,先求出其
一个质因子,假设是a1,则反复试除a1,求得b1,而在DP过程中已经求得了
N/a1的约数个数,设为g(N/a1),则g(N)=g(N/a1)*(b1+1)/b1
求出反素数后,我们就可以得到n内最大的那个反素数设为k,问题转换为如何求得
谁是第k个跳出来的人。
*/
#include <iostream>
#include <cstdio>
using namespace std;
#define maxn 500108
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
bool isp[maxn+1000];
int num[maxn+1000];
int yinshu[maxn+1000];
int maxid[maxn+1000];//用来存最大反素数的id
char Name[maxn+1000][12];
int nextt[maxn+1000];
int winnerid;
struct ST
{
	int l,r,res;
}st[4*maxn];
void init()//筛选素数
{
	isp[1]=1;
	for(int i=2;i<720;i++)
	{
		if(!isp[i])
		{
			for(int j=i*i;j<=maxn;j+=i)
			{
				isp[j]=1;
				if(!yinshu[j])
				yinshu[j]=i;
			}
		}
	}
}
void get()//这一步是求出maxn内,每个数因子数的个数
{
	num[0]=0;
	num[1]=1;
	maxid[1]=1;
	for(int i=2;i<=maxn;i++)
	{
		if(!isp[i])num[i]=2;//如果是个素数,那么他的因子数就是2
		else//如果不是个素数,那么
		{
			int s,t,ant=1;
			s=t=i/yinshu[i];//接下来我们要反复除yinshu[i].求出i的质因子yinshu[i]的指数
			while(t==yinshu[i]||(isp[t]&&yinshu[i]==yinshu[t]))
			{
				t/=yinshu[i];
				ant++;
			}
			num[i]=num[s]*(ant+1)/ant;
		}
		if(num[i]>num[maxid[i-1]])
		{
			maxid[i]=i;
		}
		else maxid[i]=maxid[i-1];
	}
}
void buildtree(int id,int l,int r)
{
	st[id].l=l;st[id].r=r;
	st[id].res=r-l+1;
	if(l==r)return;
	int mid=(l+r)>>1;
	buildtree(lson);
	buildtree(rson);
}
int update(int id,int l,int r,int p)//要来T第P个人
{
	st[id].res--;
	if(st[id].l==st[id].r)
	{
		return st[id].l;
	}
	if(st[id<<1].res>=p)
	{
		return update(id<<1,l,st[id<<1].r,p);
	}
	else return update(id<<1|1,st[id<<1|1].l,r,p-st[id<<1].res);
}
int main()
{
	int n,star,sum,p;//sum用来存此时的真实人数
	init();get();
	while(scanf("%d%d",&n,&star)==2)
	{
		sum=n;
		buildtree(1,1,n);
		for(int i=1;i<=n;i++)
		{
			scanf("%s%d",Name[i],&nextt[i]);
		}
		p=star;
		//想想如何得到要踢的人是第几个人
		for(int i=1;i<=n;i++)
		{
			int z=update(1,1,n,p);
			if(i==maxid[n])
			{
				winnerid=z;	
			}
			sum--;
			if(nextt[z]>0)//要踢左边第几个
			{
				if(sum<=1)p=1;
				else
				{	p=p-1+(nextt[z]%sum);
					if(p>sum)p-=sum;
					if(p<=0)p+=sum;
				}
			}
			else//要踢右边第几个
			{
				if(sum<=1)p=1;
				else
				{
					p=p+(nextt[z]%sum);
					if(p<=0)p+=sum;
					if(p>sum)p-=sum;
				}
			}
		}
		printf("%s %d\n",Name[winnerid],num[maxid[n]]);
	}
	return 0;
}


 

 

二:成段更新(我当初看这个也看了好久才懂!其实说白了就是一个懒惰标记,更新的时候不要更新到底,比如buildtree(1,1,n).操作是区间加减某个数,询问是区间和。必须l到r增加add.我在update的时候呢,就找到l到r这段区间

直接将其区间和+add*(r-l+1).如果不是刚好就拆两段嘛.这样在该区间的父辈区间的区间和都是正确的。而子区间的区间和我们还没更新,是错误的。当要询问其子区间的区间和的时候,我们需要将add往下传递,也就是PushDown函数的作用了。

HDU1698 Just a Hook

题意:操作有区间置1置2置3,最后询问区间和

思路:懒惰标记set,PushUp函数维护区间和.update的时候,修改更新区间的和,PushUp向上维护。PushDown函数向下传递懒惰标记同时更新子区间的和.

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 100080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
struct ST
{
	int l,r,set,key;
}st[maxn<<2];
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id]. r = r,st[id].set = 1;
	st[id].key = r - l +1;
	if(l == r)
	{
		return;
	}
	int mid = (l+r) >> 1;
	buildtree(lson);
	buildtree(rson);
}
void PushUp(int id)
{
	st[id].key = st[id<<1].key + st[id<<1|1].key;
}
void PushDown(int id)
{
	if(st[id].set != -1)
	{
		st[id<<1].set = st[id<<1|1].set = st[id].set;
		st[id<<1].key = (st[id<<1].r - st[id<<1].l + 1)*st[id].set;
		st[id<<1|1].key = (st[id<<1|1].r - st[id<<1|1].l + 1)*st[id].set;
		st[id].set = -1;
	}
}
void update(int id,int l,int r,int Set)
{
	if(st[id].l == l && st[id].r == r)
	{
		st[id].set = Set;
		st[id].key = (r - l + 1)*Set;
		return;
	}
	PushDown(id);
	if(st[id<<1].r >= r)
	{
		update(id<<1,l,r,Set);
		PushUp(id);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		update(id<<1|1,l,r,Set);
		PushUp(id);
		return;
	}
	update(id<<1,l,st[id<<1].r,Set);
	update(id<<1|1,st[id<<1|1].l,r,Set);
	PushUp(id);
}
int query(int id,int l,int r)
{
	if(st[id].l == l && st[id].r == r)
	{
		return st[id].key;
	}
	PushDown(id);
	if(st[id<<1].r >= r)
	{
		return query(id<<1,l,r);
	}
	if(st[id<<1|1].l <= l)
	{
		return query(id<<1|1,l,r);
	}
	return query(id<<1,l,st[id<<1].r) + query(id<<1|1,st[id<<1|1].l,r);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int t;
	scanf("%d",&t);
	for(int cas = 1;cas <= t;cas++)
	{
		int n;	scanf("%d",&n);
		buildtree(1,1,n);
		int m;	scanf("%d",&m);
		for(int i = 1;i <= m;i++)
		{
			int u,v,ope;
			scanf("%d%d%d",&u,&v,&ope);
			update(1,u,v,ope);
		}
		printf("Case %d: The total value of the hook is %d.\n",cas,st[1].key);
	}
	return 0;
}

POJ3468 A Simple Problem with Integers

题意:操作区间加减,询问区间和

思路:update函数更新指定区间懒惰标记和和,PushUp函数向上传递,维护区间和。PushDown函数向下传递懒惰标记的同时更新子区间的区间和.

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define LL long long int
#define maxn 100080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int A[maxn];
struct ST
{
	int l,r;
	LL key,add;
}st[maxn<<2];
void PushUp(int id)
{
	st[id].key = st[id<<1].key + st[id<<1|1].key;
}
void PushDown(int id)
{
	if(st[id].add)
	{
		st[id<<1].add += st[id].add;
		st[id<<1|1].add += st[id].add;
		st[id<<1].key += (st[id<<1].r - st[id<<1].l + 1)*st[id].add;
		st[id<<1|1].key += (st[id<<1|1].r - st[id<<1|1].l + 1)*st[id].add;
		st[id].add = 0;
	}
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r;
	if(l == r)
	{
		st[id].key = A[l];
		return;
	}
	int mid = (l + r)>>1;
	buildtree(lson);
	buildtree(rson);
	PushUp(id);
}
void update(int id,int l,int r,LL Add)
{
	if(st[id].l == l && st[id].r == r)
	{
		st[id].add += Add;
		st[id].key += (r - l + 1)*Add;
		return;
	}
	PushDown(id);
	if(st[id<<1].r >= r)	
	{
		update(id<<1,l,r,Add);
		PushUp(id);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		update(id<<1|1,l,r,Add);
		PushUp(id);
		return;
	}
	update(id<<1,l,st[id<<1].r,Add);
	update(id<<1|1,st[id<<1|1].l,r,Add);
	PushUp(id);
}
LL query(int id,int l,int r)
{
	if(st[id].l == l && st[id].r == r)	return st[id].key;
	PushDown(id);
	if(st[id<<1].r >= r)	return query(id<<1,l,r);
	if(st[id<<1|1].l <= l)	return query(id<<1|1,l,r);
	return query(id<<1,l,st[id<<1].r) + query(id<<1|1,st[id<<1|1].l,r);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int n,m;
	while(scanf("%d%d",&n,&m)==2)
	{
		for(int i = 1;i <= n;i++)	scanf("%d",&A[i]);
		buildtree(1,1,n);
		char ope[2];int u,v,w;
		for(int i = 1;i <= m;i++)
		{
			scanf("%s",ope);
			if(ope[0] == 'Q')
			{
				scanf("%d%d",&u,&v);
				printf("%lld\n",query(1,u,v));
			}
			else 
			{
				scanf("%d%d%d",&u,&v,&w);
				update(1,u,v,w);
			}
		}
	}
	return 0;
}

 

POJ2528 Mayor's posters

题意:往木条上贴海报,一张一张贴,求最后能看到的海报数目

思路:线段树的建立很好想,不过区间达到了1QW。而实际只有1W根棍子,也就是说2W个数就可以了

                离散化要注意一点,比如有数据(1,10) (1,4) (6,10)还有数据(1,10) (1,4)   (5,10)

            问题出来了,第一组数据明明是可以看到3张海报的,这样只能看到2张海报了。解决方法是对坐标排序后,如果前后两个

                的坐标差值大于1,就再插入一个点

            比如1 4 6 10 离散后就是1 2 4 5 6 7 10    那么就是(1,7) (1,3) (5,7)

                update的时候就是区间成段置值,更新颜色和懒惰标记,PushDown函数向下传递更新颜色和子区间懒惰标记,query函数就

            递归到整个区间颜色是统一的地方为止

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 40080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int X[maxn<<1];
bool vis[maxn];
int ans;
struct ST
{
	int l,r,set;
}st[maxn<<2];
struct Edge
{
	int from,to;
}edge[10080];
void PushDown(int id)
{
	if(st[id].set != - 1)
	{
		st[id<<1].set = st[id<<1|1].set = st[id].set;
		st[id].set = -1;
	}
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].set = 0;
	if(l == r)	return;
	int mid = (l + r) >> 1;
	buildtree(lson);
	buildtree(rson);
}
void update(int id,int l,int r,int Set)
{
	if(st[id].l == l && st[id].r == r)
	{
		st[id].set = Set;
		return;
	}
	PushDown(id);
	if(st[id<<1].r >= r)
	{
		update(id<<1,l,r,Set);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		update(id<<1|1,l,r,Set);
		return;
	}
	update(id<<1,l,st[id<<1].r,Set);
	update(id<<1|1,st[id<<1|1].l,r,Set);
}
void query(int id)
{
	if(st[id].set != -1)
	{
		if(!vis[st[id].set] && st[id].set != 0)	
		{
			ans++;
			vis[st[id].set] = 1;
		}
		return;
	}
	PushDown(id);
	query(id<<1);
	query(id<<1|1);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int t;	scanf("%d",&t);
	while(t--)
	{
		int n;	scanf("%d",&n);
		int k = 0;
		for(int i = 0;i < n;i++)
		{
			scanf("%d%d",&edge[i].from,&edge[i].to);
			X[k++] = edge[i].from;
			X[k++] = edge[i].to;
		}
		sort(X,X+k);
		int last = X[0],rear = k;
		for(int i = 0;i < k;i++)
		{
			if(X[i] - last > 1)	X[rear++] = last + 1;
			last = X[i];
		}
		sort(X,X+rear);
		buildtree(1,1,rear);
		int cnt = 1;
		for(int i = 0;i < n;i++)
		{
			update(1,lower_bound(X,X+rear,edge[i].from)-X+1,lower_bound(X,X+rear,edge[i].to)-X+1,cnt);
			cnt++;
		}
		ans = 0;
		memset(vis,0,sizeof(vis));
		query(1);
		printf("%d\n",ans);
	}
	return 0;
}

POJ3225 Help with Intervals

题意:

  1. U其实就是区间置1
  2. D其实就是区间置0
  3. I就是0到a-1 b+1到65535置0
  4. C就是区间取反(只剩中间部分)
  5. S就是区间取反(好像和C相同,整条)
  6. 输出最后的区间

思路:

         因为有区间开闭,所以需要拆点

        【: 2*u (:2*u+1 ):2*v-1 】:2*v

         注意下异或操作。在update的时候,置0置1都比较好处理。orr操作的话,我们先看set是否为0或1,如果是只需st[id].set^=1;

         否则st[id].orr ^= 1;query的时候,递归到st[id].set 不为-1时。用vis数组标记元素是否在集合中。

PS:注意下(3,3)这种数据

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 158888
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
bool vis[maxn];
struct ST
{
	int l,r,set,orr;
}st[maxn<<2];
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].set = 0,st[id].orr = 0;
	if(l == r)	return;
	int mid = (l+r)>>1;
	buildtree(lson);
	buildtree(rson);
}
void PushDown(int id)
{
	if(st[id].set != -1)
	{
		st[id<<1].set = st[id<<1|1].set = st[id].set;
		st[id<<1].orr = st[id<<1|1].orr = 0;
		st[id].set = -1;
	}
	if(st[id].orr)
	{
		if(st[id<<1].set != -1)	st[id<<1].set ^= 1;
		else st[id<<1].orr ^= 1;
		if(st[id<<1|1].set != -1)	st[id<<1|1].set ^= 1;
		else st[id<<1|1].orr ^= 1;
		st[id].orr = 0;
	}
}
void update(int id,int l,int r,int ope)
{
	if(st[id].l == l && st[id].r == r)
	{
		if(ope != 2)
		{
			st[id].orr = 0;
			st[id].set = ope;
			return;
		}
		else
		{
			if(st[id].set != -1)	st[id].set ^= 1;
			else st[id].orr ^= 1;
			return;
		}
	}
	PushDown(id);
	if(st[id<<1].r >= r)
	{
		update(id<<1,l,r,ope);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		update(id<<1|1,l,r,ope);
		return;
	}
	update(id<<1,l,st[id<<1].r,ope);
	update(id<<1|1,st[id<<1|1].l,r,ope);
}
void query(int id)
{
	if(st[id].set != -1)
	{
		if(st[id].set == 1)
		{
			for(int i = st[id].l;i <= st[id].r;i++)	vis[i] =1;
		}
		return;
	}
	PushDown(id);
	query(id<<1);
	query(id<<1|1);
}
int main()
{
	//freopen("in.txt","r",stdin);
	char ope[2];
	buildtree(1,1,140000);
	while(scanf("%s",ope)!=EOF)
	{
		char left,righ;
		int u,v;
		getchar();
		left = getchar();
		scanf("%d,%d",&u,&v);
		righ = getchar();
		u++;v++;
		if(left == '(')	u = 2*u+1;
		else u = u*2;
		if(righ == ')')	v = 2*v-1;
		else v = 2*v;
		if(ope[0] == 'U' && v>=u)	update(1,u,v,1);
		if(ope[0] == 'D' && v>=u)	update(1,u,v,0);
		if(ope[0] == 'S' && v>=u)	update(1,u,v,2);
		if(ope[0] == 'C')	
		{
			update(1,1,u-1,0);
			update(1,v+1,140000,0);
			if(v>=u)update(1,u,v,2);
		}
		if(ope[0] == 'I')
		{
			update(1,1,u-1,0);
			update(1,v+1,140000,0);
		}
	}
	memset(vis,0,sizeof(vis));
	query(1);
	int ok = 1;
	bool flag = true;
	int first,last;
	for(int i = 1;i <= 140000;i++)
	{
		if(vis[i])	
		{
			flag = false;
			if(vis[i-1])	last = i;
			else first = i;
		}
		else 
		{
			if(vis[i-1])	
			{
				if((first)&1) printf("(%d,",first/2-1);
				else printf("[%d,",first/2-1);
				if((i-1)&1)	printf("%d) ",i/2-1);
				else printf("%d] ",i/2-1);
			}
		}
	}
	if(flag)	printf("empty set");
	printf("\n");
	return 0;
}


POJ1436 Horizontally Visible Segments

题意:给出垂直线段,两条线段能连一条不经过其它线的水平线视为可见,3条两两可见的线段构成一三角形,求三角形数目

思路:将线段从左到右排序,query函数将该线段所能见到的线段放入其集合中,最后暴力枚举即可。(我代码WA,看了N久没看出毛病。几天后重新看还是觉得没问题!看出来的帮忙指正!)

/*
Y坐标要乘以2,比如(1,3)到(4,5)这两段中间是空着的,但是表示不出来
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
#define maxn 40080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
set <int> coll[maxn];
set <int> :: iterator it;
set <int> :: iterator itt;
struct Edge
{
	int u,v,x;
}edge[maxn];
bool cmp(Edge a,Edge b)
{
	return a.x < b.x;
}
struct ST
{
	int l,r,set;
}st[maxn<<2];
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].set = 0;
	if(l == r)	return;
	int mid = (l + r)>>1;
	buildtree(lson);
	buildtree(rson);
}
void PushDown(int id)
{
	if(st[id].set > 0)
	{
		st[id<<1].set = st[id<<1|1].set = st[id].set;
		st[id].set = 0;
	}
}
void update(int id,int l,int r,int cnt)
{
	if(st[id].l == l && st[id].r == r)
	{
		st[id].set = cnt;
		return;
	}
	PushDown(id);
	if(st[id<<1].r >= r)	
	{
		update(id<<1,l,r,cnt);
		return;
	}
	if(st[id<<1|1].l <= l)	
	{
		update(id<<1|1,l,r,cnt);
		return;
	}
	update(id<<1,l,st[id<<1].r,cnt);
	update(id<<1|1,st[id<<1|1].l,r,cnt);
}
void query(int id,int l,int r,int num)
{
	if(st[id].set || l == r)
	{
		if(st[id].set != 0)
		coll[num].insert(st[id].set);
		return;
	}
	PushDown(id);
	if(st[id<<1].r >= r)
	{
		query(id<<1,l,r,num);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		query(id<<1|1,l,r,num);
		return;
	}
	query(id<<1,l,st[id<<1].r,num);
	query(id<<1|1,st[id<<1|1].l,r,num);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int t;	scanf("%d",&t);
	while(t--)
	{
		int n;	scanf("%d",&n);
		buildtree(1,0,30000);
		for(int i = 1;i <= n;i++)
		{
			coll[i].clear();
			int x,y1,y2;
			scanf("%d%d%d",&y1,&y2,&x);
			//y1++;y2++;
			y1 *= 2;y2 *= 2;
			edge[i].u = y1,edge[i].v = y2,edge[i].x = x;
		}
		sort(edge+1,edge+n+1,cmp);
		for(int i = 1;i <= n;i++)
		{
			query(1,edge[i].u,edge[i].v,i);
			update(1,edge[i].u,edge[i].v,i);
		}
		int ans = 0;
		for(int i = 3;i <= n;i++)
		{
			for(it = coll[i].begin();it != coll[i].end();it++)
			{
				int tt = *it;
				for(itt = coll[tt].begin();itt != coll[tt].end();itt++)
				{
					if(coll[i].find(*itt) != coll[i].end())	ans++;
				}
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

POJ2991 Crane
题意:初始N节木棍置于Y轴,木棍与木棍间有节点,可旋转,求每次旋转某节点后终点位置

思路:将线段看成向量,则旋转角度后的向量可以如此表示。证明很简单,

自己用三角函数随便搞搞。这样懒惰标记存角度,PushUp函数维护向量; 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
#define maxn 20080
#define PI acos(-1.0)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
double len[maxn];
int du[maxn];
struct ST
{
	double x,y;
	int l,r,add;
}st[maxn<<2];
void rotate(int id,int add)
{
	double a = double(add)/180*PI;
	double x = st[id].x,y = st[id].y;
	st[id].x = cos(a)*x - sin(a)*y;
	st[id].y = cos(a)*y +sin(a)*x;
}
void PushUp(int id)
{
	st[id].x = st[id<<1].x + st[id<<1|1].x;
	st[id].y = st[id<<1].y + st[id<<1|1].y;
}
void PushDown(int id)
{
	if(st[id].add)
	{
		st[id<<1].add += st[id].add;
		st[id<<1|1].add += st[id].add;
		rotate(id<<1,st[id].add);
		rotate(id<<1|1,st[id].add);
		st[id].add = 0;
	}
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].add = 0;
	if(l == r)	
	{
		st[id].x = 0,st[id].y = len[l];
		return;
	}
	int mid = (l + r)>>1;
	buildtree(lson);
	buildtree(rson);
	PushUp(id);
}
void update(int id,int l,int r,int add)
{
	if(st[id].l == l && st[id].r == r)
	{
		st[id].add += add;
		rotate(id,add);
		return;
	}
	PushDown(id);
	if(st[id<<1].r >= r)
	{
		update(id<<1,l,r,add);
		PushUp(id);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		update(id<<1|1,l,r,add);
		PushUp(id);
		return;
	}
	update(id<<1,l,st[id<<1].r,add);
	update(id<<1|1,st[id<<1|1].l,r,add);
	PushUp(id);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int n,m;
	int cas = 0;
	while(scanf("%d%d",&n,&m)==2)
	{
		if(cas != 0)	printf("\n");	cas++;
		for(int i = 0;i <= n;i++)	du[i] = 180;
		//du[0] = 90;
		for(int i = 1;i <= n;i++)	scanf("%lf",&len[i]);
		buildtree(1,1,n);
		for(int i = 1;i <= m;i++)
		{
			int u,d;
			scanf("%d%d",&u,&d);
			update(1,u+1,n,d-du[u+1]);
			du[u+1] = d;
			printf("%.2lf %.2lf\n",st[1].x,st[1].y);
		}
	}
	return 0;
}
			

 
三:区间合并(这类题目经常要满足连续一定长度的区间,所以PushUp函数需要将左右儿子进行合并。经常要第一个满足的区间,所以query优先左儿子,不行再看左右合并,再不行才去右儿子找)
 
题意:旅客住酒店,操作1:要连续长度的房间,返回首房间位置 操作2:区间置0
思路:节点维护三个值,key最长连续区间长度,lkey,最长左边连续区间长度,rkey最长右边连续区间长度,还需要懒惰标记set,用来区间置0,置1
           update函数也就是区间置0,置1,同时更新key,lkey,rkey.
           PushDown函数就是将懒惰标记下传,同时更新儿子的三个权值
           PushUp函数是关键,rkey为例,rkey = st[id<<1|1].rkey. if(st[id<<1|1].rkey == st[id<<1|1].r - st[id<<1|1].l + 1) rkey += st[id<<1].rkey;
                              st[id].key = max(st[id<<1].key,st[id<<1|1].key,st[id<<1].rkey + st[id<<1|1].lkey);
                              st[id].rkey = (st[id<<1|1].rkey == st[id<<1|1].r - st[id<<1|1].l +1? st[id<<1].rkey + st[id<<1|1].key : st[id<<1|1].rkey);
                              st[id].lkey = (st[id<<1].lkey == st[id<<1].r - st[id<<1].l + 1?st[id<<1].key + st[id<<1|1].lkey : st[id<<1].lkey);
          query函数,先看左儿子。不行看左右合并。再不行看右儿子
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 200080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
struct ST
{
	int l,r,key,lkey,rkey,set;
}st[maxn<<2];
inline int max(int a,int b,int c)
{
	if(a<b)	a = b;
	if(a<c)		a = c;
	return a;
}
void PushUp(int id)
{
	st[id].key = max(st[id<<1].key,st[id<<1|1].key,st[id<<1].rkey + st[id<<1|1].lkey);
	st[id].rkey = (st[id<<1|1].rkey == st[id<<1|1].r - st[id<<1|1].l +1? st[id<<1].rkey + st[id<<1|1].key : st[id<<1|1].rkey);
	st[id].lkey = (st[id<<1].lkey == st[id<<1].r - st[id<<1].l + 1?st[id<<1].key + st[id<<1|1].lkey : st[id<<1].lkey);
}
void PushDown(int id)
{
	if(st[id].set != -1)
	{
		st[id<<1].set = st[id<<1|1].set = st[id].set;
		st[id<<1].key = st[id<<1].rkey = st[id<<1].lkey = st[id].set?0:st[id<<1].r - st[id<<1].l + 1;
		st[id<<1|1].key = st[id<<1|1].rkey = st[id<<1|1].lkey = st[id].set?0:st[id<<1|1].r - st[id<<1|1].l + 1;
		st[id].set = -1;
	}
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].set = 0;
	st[id].lkey = st[id].rkey = st[id].key = r - l + 1;
	if(l == r)	return;
	int mid = (l + r) >> 1;
	buildtree(lson);
	buildtree(rson);
}
void update(int id,int l,int r,int ope)
{
	if(st[id].l == l && st[id].r == r)
	{
		st[id].set = ope;
		st[id].key = st[id].lkey = st[id].rkey = ope?0:r - l + 1;
		return;
	}
	PushDown(id);
	if(st[id<<1].r >= r)
	{
		update(id<<1,l,r,ope);
		PushUp(id);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		update(id<<1|1,l,r,ope);
		PushUp(id);
		return;
	}
	update(id<<1,l,st[id<<1].r,ope);
	update(id<<1|1,st[id<<1|1].l,r,ope);
	PushUp(id);
}
int query(int id,int l,int r,int cost)
{
	if(l == r)	return l;
	PushDown(id);
	if(st[id<<1].key >= cost)
	{
		return query(id<<1,l,st[id<<1].r,cost);
	}
	else if(st[id<<1].rkey + st[id<<1|1].lkey >= cost)
	{
		return st[id<<1].r - st[id<<1].rkey + 1;
	}
	else return query(id<<1|1,st[id<<1|1].l,r,cost);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int n,m,pos;
	while(scanf("%d%d",&n,&m)==2)
	{
		buildtree(1,1,n);
		int ope,u,v;
		for(int i=1;i<=m;i++)
		{
			scanf("%d",&ope);
			if(ope==1)
			{
				scanf("%d",&u);
				if(u>st[1].key)printf("%d\n",0);
				else
				{
					pos=query(1,1,n,u);
					printf("%d\n",pos);
					update(1,pos,pos+u-1,1);
				}
			}
			else 
			{
				scanf("%d%d",&u,&v);
				update(1,u,u+v-1,0);
			}
		}
	}
	return 0;
}


HDU3308 LCIS
 
题意:操作,点权修改。询问:区间的最长连续上升子序列
思路:同样节点存三个权,key,lkey,rkey.
           PushUp函数左右儿子合并的条件是A[st[id<<1|1].l] > A[st[id<<1].r]
           query的时候有个trick.假设可合并的情况,以左边为例,min(st[id<<1].rkey,st[id<<1].r - l + 1)...
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 100080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int A[maxn];
struct ST
{
    int l,r,key,lkey,rkey;
}st[maxn<<2];
inline int max(int a,int b,int c)
{
    if(a<b) a = b;
    if(a<c)     a = c;
    return a;
}
inline int min(int a,int b)
{
    return a>b?b:a;
}
void PushUp(int id)
{
    st[id].key = max(st[id<<1].key,st[id<<1|1].key,A[st[id<<1|1].l]>A[st[id<<1].r]?st[id<<1|1].lkey + st[id<<1].rkey:0);
    st[id].rkey = max(st[id<<1|1].rkey,(st[id<<1|1].rkey == st[id<<1|1].r - st[id<<1|1].l + 1 && A[st[id<<1|1].l]>A[st[id<<1].r])?st[id<<1].rkey + st[id<<1|1].key : 0);
    st[id].lkey = max(st[id<<1].lkey,(st[id<<1].lkey == st[id<<1].r - st[id<<1].l + 1 && A[st[id<<1|1].l]>A[st[id<<1].r])? st[id<<1].key + st[id<<1|1].lkey:0);
}
void buildtree(int id,int l,int r)
{
    st[id].l = l,st[id].r = r;
    if(l == r)
    {
        st[id].key = st[id].lkey = st[id].rkey = 1;
        return;
    }
    int mid = (l+r)>>1;
    buildtree(lson);
    buildtree(rson);
    PushUp(id);
}
int query(int id,int l,int r)
{
    if(st[id].l == l && st[id].r == r)    return st[id].key;
    if(st[id<<1].r >= r)    return query(id<<1,l,r);
    if(st[id<<1|1].l <= l)    return query(id<<1|1,l,r);
    int left = min(st[id<<1].rkey,st[id<<1].r - l + 1);
    int right = min(st[id<<1|1].lkey,r - st[id<<1|1].l + 1);
    int ans = max(query(id<<1,l,st[id<<1].r),query(id<<1|1,st[id<<1|1].l,r));
    ans = max(ans,A[st[id<<1|1].l] > A[st[id<<1].r]?left + right:0);
    return ans;
}
void update(int id,int pos,int newk)
{
    if(st[id].l == pos && st[id].r == pos)
    {
        A[pos] = newk;
        return;
    }
    if(st[id<<1].r >= pos)    update(id<<1,pos,newk);
    else update(id<<1|1,pos,newk);
    PushUp(id);
}
int main()
{
    //freopen("in.txt","r",stdin);
    int t;    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i = 1;i <= n;i++)    scanf("%d",&A[i]);
        buildtree(1,1,n);
        char ope[2];int u,v;
        for(int i = 1;i <= m;i++)
        {
            scanf("%s%d%d",ope,&u,&v);
            if(ope[0] == 'Q')    printf("%d\n",query(1,u+1,v+1));
            else update(1,u+1,v);
        }
    }
    return 0;
}

HDU3397 Sequence operation
题意:5个操作,区间置01,,区间异或,询问1个数及最长连续1
思路:为了维护连续1个数,区间异或的时候知道st[id].set!=-1的地方。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 100080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int A[maxn];
struct ST
{
	int l,r,sum,lkey,rkey,key,set,orr;
}st[maxn<<2];
inline int max(int a,int b,int c)
{
	if(a<b)	a = b;
	if(a<c)		a = c;
	return a;
}
inline int max(int a,int b)
{
	return a>b?a:b;
}
inline int min(int a,int b)
{
	return a>b?b:a;
}
void PushDown(int id)
{
	if(st[id].set != -1)
	{
		st[id<<1].orr = st[id<<1|1].orr = 0;
		st[id<<1].set = st[id<<1|1].set = st[id].set;
		st[id<<1].key = st[id<<1].lkey = st[id<<1].rkey = st[id<<1].sum = st[id].set?st[id<<1].r - st[id<<1].l + 1:0;
		st[id<<1|1].key = st[id<<1|1].lkey = st[id<<1|1].rkey = st[id<<1|1].sum = st[id].set?st[id<<1|1].r - st[id<<1|1].l + 1:0;
		st[id].set = -1;
	}
	if(st[id].orr)
	{
		if(st[id<<1].set!=-1)	st[id<<1].set ^= 1;
		else st[id<<1].orr ^= 1;
		if(st[id<<1|1].set!=-1)	st[id<<1|1].set ^= 1;
		else st[id<<1].orr ^= 1;
	}
}
void PushUp(int id)
{
	st[id].key = max(st[id<<1].key,st[id<<1|1].key,st[id<<1].rkey + st[id<<1|1].lkey);
	st[id].lkey = max(st[id<<1].lkey,st[id<<1].lkey == st[id<<1].r - st[id<<1].l + 1?st[id<<1].sum + st[id<<1|1].lkey:0);
	st[id].rkey = max(st[id<<1|1].rkey,st[id<<1|1].rkey == st[id<<1|1].r - st[id<<1|1].l + 1?st[id<<1|1].sum + st[id<<1].rkey:0);
	st[id].sum = st[id<<1].sum + st[id<<1|1].sum;
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].set = -1,st[id].orr = 0;
	if(l == r)
	{
		st[id].key = st[id].lkey = st[id].rkey = st[id].sum = st[id].set = A[l];
		return;
	}
	int mid = (l + r)>>1;
	buildtree(lson);
	buildtree(rson);
	PushUp(id);
}
void update(int id,int l,int r,int ope)
{
	if(st[id].l == l && st[id].r == r)
	{
		if(ope == 0)
		{
			st[id].set = 0;st[id].orr = 0;
			st[id].key = st[id].lkey = st[id].rkey = st[id].sum = 0;
			return;
		}
		if(ope == 1)
		{
			st[id].set = 1;st[id].orr = 0;
			st[id].key = st[id].lkey = st[id].rkey = st[id].sum = r - l + 1;
			return;
		}
		if(ope == 2)
		{
			if(st[id].set != -1)
			{
				st[id].set ^= 1;st[id].orr = 0;
				st[id].key = st[id].lkey = st[id].rkey = st[id].sum = st[id].set?r - l + 1:0;
				return;
			}
		}
	}
	PushDown(id);
	if(st[id<<1].r >= r)
	{
		update(id<<1,l,r,ope);
		PushUp(id);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		update(id<<1|1,l,r,ope);
		PushUp(id);
		return;
	}
	update(id<<1,l,st[id<<1].r,ope);
	update(id<<1|1,st[id<<1|1].l,r,ope);
	PushUp(id);
}
int query(int id,int l,int r,int ope)
{
	if(st[id].l == l && st[id].r == r)
	{
		if(ope == 3)	return st[id].sum;
		if(ope == 4)	return st[id].key;
	}
	PushDown(id);
	if(st[id<<1].r >= r)
	{
		return query(id<<1,l,r,ope);
	}
	if(st[id<<1|1].l <= l)
	{
		return query(id<<1|1,l,r,ope);
	}
	if(ope == 3)
		return query(id<<1,l,st[id<<1].r,ope) + query(id<<1|1,st[id<<1|1].l,r,ope);
	if(ope == 4)
	{
		int left = query(id<<1,l,st[id<<1].r,ope);
		int right = query(id<<1|1,st[id<<1|1].l,r,ope);
		int ans = min(st[id<<1].r - l +1,st[id<<1].rkey) + min(r- st[id<<1|1].l +1,st[id<<1|1].lkey);
		return max(ans,left,right);
	}
}
int main()
{
	//freopen("in.txt","r",stdin);
	int t;	scanf("%d",&t);
	while(t--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i = 1;i <= n;i++)	scanf("%d",&A[i]);
		buildtree(1,1,n);
		for(int i = 1;i <= m;i++)
		{
			int ope,u,v;
			scanf("%d%d%d",&ope,&u,&v);
			if(ope == 3 || ope == 4)	printf("%d\n",query(1,u+1,v+1,ope));
			else update(1,u+1,v+1,ope);
		}
	}
	return 0;
}

HDU2871 Memory Control
题意:1重置所有内存 2新开连续内存 3释放X所在的内存块 4求第X个内存块的起始位置
思路:这道题用vector,每次开连续快二分vector找地方把边放进去,每次释放就erase一下。始终保持vector的有序。
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
#define maxn 50080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
inline int max(int a,int b)
{
	return a>b?a:b;
}
inline int max(int a,int b,int c)
{
	if(a < b) a = b;
	if(a < c) a = c;
	return a;
}
inline int min(int a,int b)
{
	return a>b?b:a;
}
struct ST
{
	int l,r,key,lkey,rkey,set;
}st[maxn<<2];
struct Edge
{
	int u,v;
	Edge(){}
	Edge(int uu,int vv)
	{
		u = uu;v = vv;
	}
};
vector <Edge> coll;
int binary(int x)
{
	int l = 0,r = coll.size()-1;
	while(l <= r)
	{
		int mid = (l + r)>>1;
		if(coll[mid].u <= x)	l = mid+1;
		else r = mid-1;
	}
	return l;
}
void PushDown(int id)
{
	if(st[id].set != -1)
	{
		st[id<<1].set = st[id<<1|1].set = st[id].set;
		st[id<<1].key = st[id<<1].lkey = st[id<<1].rkey = st[id].set?0:st[id<<1].r - st[id<<1].l + 1;
		st[id<<1|1].key = st[id<<1|1].lkey = st[id<<1|1].rkey = st[id].set?0:st[id<<1|1].r - st[id<<1|1].l + 1;
		st[id].set = -1;
	}
}
void PushUp(int id)
{
	st[id].key = max(st[id<<1].key,st[id<<1|1].key,st[id<<1].rkey + st[id<<1|1].lkey);
	st[id].lkey = max(st[id<<1].lkey,st[id<<1].lkey == st[id<<1].r - st[id<<1].l + 1?st[id<<1].key + st[id<<1|1].lkey:0);
	st[id].rkey = max(st[id<<1|1].rkey,st[id<<1|1].rkey == st[id<<1|1].r - st[id<<1|1].l + 1?st[id<<1|1].key + st[id<<1].rkey:0);
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].set = 0;
	st[id].key = st[id].lkey = st[id].rkey = r - l + 1;
	if(l == r)	return;
	int mid = (l + r)>>1;
	buildtree(lson);
	buildtree(rson);
}
void update(int id,int l,int r,int ope)//清空
{
	if(st[id].l == l && st[id].r == r)
	{
		st[id].set = ope;
		st[id].key = st[id].lkey = st[id].rkey = ope?0:r - l + 1;
		return;
	}
	PushDown(id);
	if(st[id<<1].r >= r)
	{
		update(id<<1,l,r,ope);
		PushUp(id);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		update(id<<1|1,l,r,ope);
		PushUp(id);
		return;
	}
	update(id<<1,l,st[id<<1].r,ope);
	update(id<<1|1,st[id<<1|1].l,r,ope);
	PushUp(id);
}
int query(int id,int l,int r,int cost)
{
	if(l == r)	return l;
	PushDown(id);
	if(st[id<<1].key >= cost)	
		return query(id<<1,l,st[id<<1].r,cost);
	if(st[id<<1].rkey + st[id<<1|1].lkey >= cost)
		return st[id<<1].r - st[id<<1].rkey + 1;
	return query(id<<1|1,st[id<<1|1].l,r,cost);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int n,m;
	while(scanf("%d%d",&n,&m)==2)
	{
		buildtree(1,1,n);
		coll.clear();	
		int u;	char ope[10];
		for(int i = 1;i <= m;i++)
		{
			scanf("%s",ope);
			if(ope[0] != 'R')	scanf("%d",&u);
			if(ope[0] == 'R')
			{
				coll.clear();
				update(1,1,n,0);
				printf("Reset Now\n");
			}
			if(ope[0] == 'N')
			{
				if(st[1].key >= u)
				{
					int s = query(1,1,n,u);
					update(1,s,s+u-1,1);
					//二分找地方插入
					if(coll.size() == 0)	coll.push_back(Edge(s,s+u-1));
					else 
					{
						int hehe = binary(s);
						coll.insert(coll.begin()+hehe,Edge(s,s+u-1));//插入比如2就是2这个变成新的,其他后推
					}
					printf("New at %d\n",s);
				}
				else printf("Reject New\n");
			}
			if(ope[0] == 'F')
			{
				int pos = binary(u)-1;
				if(pos < coll.size() && coll[pos].u <= u && coll[pos].v >= u)
				{
					update(1,coll[pos].u,coll[pos].v,0);
					printf("Free from %d to %d\n",coll[pos].u,coll[pos].v);
					coll.erase(coll.begin()+pos);
				}
				else printf("Reject Free\n");
			}
			if(ope[0] == 'G')
			{
				if(coll.size() > u - 1)
				printf("Get at %d\n",coll[u-1].u);
				else printf("Reject Get\n");
			}
		}
		printf("\n");
	}
	return 0;
}

HDU1540 Tunnel Warfare
题意:求包含指定点的最长连续区间
思路:节点存2个权,lkey和rkey.query的时候,比如u这个点,就query(1,1,u,2)+query(1,u,n,1).注意考虑本身可能+了两次。
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 50080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int pre[maxn];
struct ST
{
	int l,r,lkey,rkey;
}st[maxn<<2];
inline int max(int a,int b)
{
	return a>b?a:b;
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r;
	st[id].lkey = st[id].rkey = r-l+1;
	if(l == r)	return;
	int mid = (l +r) >> 1;
	buildtree(lson);
	buildtree(rson);
}
void PushUp(int id)
{
	st[id].lkey = max(st[id<<1].lkey,st[id<<1].r - st[id<<1].l + 1==st[id<<1].lkey?st[id<<1].lkey + st[id<<1|1].lkey:0);
	st[id].rkey = max(st[id<<1|1].rkey,st[id<<1|1].r - st[id<<1|1].l + 1 == st[id<<1|1].rkey?st[id<<1].rkey + st[id<<1|1].rkey:0);
}
void update(int id,int pos,int ope)
{
	if(st[id].l == pos && st[id].r == pos)
	{
		st[id].lkey = st[id].rkey = ope;
		return;
	}
	if(st[id<<1].r >= pos)	update(id<<1,pos,ope);
	if(st[id<<1|1].l <= pos)	update(id<<1|1,pos,ope);
	PushUp(id);
}
int query(int id,int l,int r,int ope)
{
	if(st[id].l == l && st[id].r == r)
	{
		if(ope == 1)	return st[id].lkey;
		else return st[id].rkey;
	}
	if(st[id<<1].r >= r)
	{
		return query(id<<1,l,r,ope);
	}
	if(st[id<<1|1].l <= l)
	{
		return query(id<<1|1,l,r,ope);
	}
	int ans1 = query(id<<1,l,st[id<<1].r,ope);
	int ans2 = query(id<<1|1,st[id<<1|1].l,r,ope);
	if(ope == 1)
	{
		if(ans1 == st[id<<1].r - l + 1)	return ans1+ans2;
		return ans1;
	}
	else 
	{
		if(ans2 == r - st[id<<1|1].l + 1)	return ans1+ans2;
		return ans2;
	}
}
int main()
{
	//freopen("in.txt","r",stdin);
	int n,m;
	while(scanf("%d%d",&n,&m)==2)
	{
		buildtree(1,1,n);
		memset(pre,0,sizeof(pre));
		char ope[2];int u,last = 0;
		for(int i = 1;i <= m;i++)
		{
			scanf("%s",ope);
			if(ope[0] != 'R')	scanf("%d",&u);
			if(ope[0] == 'D')	
			{
				pre[u] = last;
				last = u;
				update(1,u,0);
			}
			if(ope[0] == 'Q')	
			{
				int ans = query(1,1,u,2) + query(1,u,n,1) - 1;
				printf("%d\n",ans>0?ans:0);
			}
			if(ope[0] == 'R')
			{
				update(1,last,1);
				last = pre[last];
			}
		}
	}
	return 0;
}

四:扫描线
题意:矩形面积并
思路:对于每个矩形,用上下两条边代替矩形,下边vis为1,上边vis为-1,将所出现的所有横坐标用X数组存起来,排序。
           然后将edge按y从小到大排序。接着便是遍历每条边了。lastlen表示上一次更新后边的总长度,扫描边的过程area+=lastlen*(edge[i].y - lasty);
           这么说吧,这里X是double型的,我们离散化才能建线段树,或者X太大的时候也要离散化。也就是说,我们建树的时候,用排序后的X的下标去建树
           我们要得到的是长度,st[id].len = X[st[id].r] - X[st[id].l];即可。但是我们要怎么确保得到整个区间的正确长度呢?这道题数据水,更新到底也能过,
           但实际上,if(st[id].add > 0)st[id].len = X[st[id].r] - X[st[id].l];
                             else if(st[id].l + 1 != st[id].r) st[id].len = st[id<<1].len + st[id<<1|1].len;
                             else st[id].len = 0;
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 280
#define lson id<<1,l,mid
#define rson id<<1|1,mid,r
double X[maxn];
struct ST
{
	int l,r,add;
	double len;
}st[maxn<<2];
struct Edge
{
	double x1,x2,y;
	int add;
}edge[maxn];
bool cmp(Edge a,Edge b)
{
	return a.y < b.y;
}
void PushUp(int id)
{
	if(st[id].add >= 1)		st[id].len = X[st[id].r] - X[st[id].l];
	else if(st[id].l + 1 != st[id].r)	st[id].len = st[id<<1].len + st[id<<1|1].len;
	else st[id].len = 0;
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].len = 0;
	if(l + 1 == r)	return;
	int mid = (l+r) >> 1;
	buildtree(lson);
	buildtree(rson);
}
void update(int id,int l,int r,int add)
{
	if(st[id].l == l && st[id].r == r)
	{
		st[id].add += add;
		if(st[id].add >= 1)		st[id].len = X[r] - X[l];
		else if(l + 1 != r)	st[id].len = st[id<<1].len + st[id<<1|1].len;
		else st[id].len = 0;
		return;
	}
	if(st[id<<1].r >= r)
	{
		update(id<<1,l,r,add);
		PushUp(id);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		update(id<<1|1,l,r,add);
		PushUp(id);
		return;
	}
	update(id<<1,l,st[id<<1].r,add);
	update(id<<1|1,st[id<<1|1].l,r,add);
	PushUp(id);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int n,cas = 0;
	while(scanf("%d",&n)!=EOF && n)
	{
		double x1,y1,x2,y2;
		int k = 0,cnt = 0;
		for(int i = 1;i <= n;i++)
		{
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			edge[cnt].add = 1;
			edge[cnt].x1 = x1;
			edge[cnt].x2 = x2;
			edge[cnt++].y = y1;
			edge[cnt].add = -1;
			edge[cnt].x1 = x1;
			edge[cnt].x2 = x2;
			edge[cnt++].y = y2;
			X[k++] = x1;
			X[k++] = x2;
		}
		sort(edge,edge+cnt,cmp);
		sort(X,X+k);
		buildtree(1,0,k-1);
		double len = 0,ans = 0,lasty = 0;
		for(int i = 0;i < cnt;i++)
		{
			ans += len*(edge[i].y - lasty);
			int u = lower_bound(X,X+k,edge[i].x1) - X;
			int v = lower_bound(X,X+k,edge[i].x2) - X;
			update(1,u,v,edge[i].add);
			len = st[1].len;
			lasty = edge[i].y;
		}
		printf("Test case #%d\n",++cas);
		printf("Total explored area: %.2lf\n\n",ans);
	}
	return 0;
}
题意:矩形周长并
思路:会了矩形面积并再来理解矩形周长并就不难了,显然按照扫描线的做法,从下往上扫描,周长的增加无非分为X周和Y周。
           X周的st[1].len的改变量也就是了,Y周的话看edge[i].y - lasty,还得看有几段线段,线段数*2*(edge[i].y - lasty)即可。
           问题难点在于如何知道有几段线段,我们用sum表示区间的线段数,用lcov和rcov表示左右端点是否被覆盖。则
           PushUp的时候,要更新父节点的len,sum,lcov 和 rcov.
          
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define maxn 10080
#define lson id<<1,l,mid
#define rson id<<1|1,mid,r
int X[maxn];
struct ST
{
    int l,r,lcov,rcov,sum,add,len;
}st[maxn<<2];
struct Edge
{
    int x1,x2,y;
    int add;
}edge[maxn];
bool cmp(Edge a,Edge b)
{
    return a.y < b.y;
}
void PushUp(int id)
{
    if(st[id].add > 0)    
    {
        st[id].len = X[st[id].r] - X[st[id].l];
        st[id].lcov = st[id].rcov = 1;
        st[id].sum = 1;
    }
    else if(st[id].l + 1 != st[id].r)    
    {
        st[id].lcov = st[id<<1].lcov;
        st[id].rcov = st[id<<1|1].rcov;
        st[id].len = st[id<<1].len + st[id<<1|1].len;
        st[id].sum = st[id<<1].sum + st[id<<1|1].sum - (st[id<<1].rcov && st[id<<1|1].lcov);
    }
    else 
    {
        st[id].len = st[id].sum = st[id].lcov = st[id].rcov = 0;
    }
}
void buildtree(int id,int l,int r)
{
    st[id].l = l,st[id].r = r,st[id].lcov = st[id].rcov = st[id].sum = st[id].len = st[id].add = 0;
    if(l + 1 == r)    return;
    int mid = (l + r)>>1;
    buildtree(lson);
    buildtree(rson);
}
void update(int id,int l,int r,int add)
{
    if(st[id].l == l && st[id].r == r)
    {
        st[id].add += add;
        PushUp(id);
        return;
    }
    if(st[id<<1].r >= r)
    {
        update(id<<1,l,r,add);
        PushUp(id);
        return;
    }
    if(st[id<<1|1].l <= l)
    {
        update(id<<1|1,l,r,add);
        PushUp(id);
        return;
    }
    update(id<<1,l,st[id<<1].r,add);
    update(id<<1|1,st[id<<1|1].l,r,add);
    PushUp(id);
}
int main()
{
    //freopen("in.txt","r",stdin);
    int n;
    while(scanf("%d",&n)!=EOF && n)
    {
        int cnt = 0,k = 0;
        for(int i = 0;i < n;i++)
        {
            int x1,x2,y1,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            edge[cnt].x1 = x1;
            edge[cnt].x2 = x2;
            edge[cnt].y = y1;
            edge[cnt++].add = 1;
            edge[cnt].x1 = x1;
            edge[cnt].x2 = x2;
            edge[cnt].y = y2;
            edge[cnt++].add = -1;
            X[k++] = x1;
            X[k++] = x2;
        }
        sort(edge,edge+cnt,cmp);
        sort(X,X+k);
        buildtree(1,0,k-1);
        int ans = 0,lastlen = 0,lasty = 0;
        for(int i = 0;i < cnt;i++)
        {
            int u = lower_bound(X,X+k,edge[i].x1) - X;
            int v = lower_bound(X,X+k,edge[i].x2) - X;
            ans += st[1].sum* (edge[i].y - lasty)*2;
            update(1,u,v,edge[i].add);
            ans += abs(st[1].len - lastlen);
            lastlen = st[1].len;
            lasty = edge[i].y;
        }
        printf("%d\n",ans);
    }
    return 0;
}
题意:求有矩形洞的矩形海报的矩形面积并
思路:将矩形拆成4个矩形就行。。注意不是一直能拆成4个,得特判,不然RE或者TLE到死~!!
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 200080
#define lson id<<1,l,mid
#define rson id<<1|1,mid,r
#define LL long long int
int X[maxn];
struct ST
{
    int l,r,add,len;
}st[maxn<<2];
struct Edge
{
    int x1,x2,y,vis;
}edge[10*maxn];
bool cmp(Edge a,Edge b)
{
    return a.y < b.y;
}
void buildtree(int id,int l,int r)
{
    st[id].l = l,st[id].r = r;
    st[id].len = st[id].add = 0;
    if(l + 1 == r)    return;
    int mid = (l + r) >> 1;
    buildtree(lson);
    buildtree(rson);
}
void PushUp(int id)
{
    if(st[id].add > 0)    st[id].len = X[st[id].r] - X[st[id].l];
    else if(st[id].l + 1 != st[id].r)
    {
        st[id].len = st[id<<1].len + st[id<<1|1].len;
    }
    else st[id].len = 0;
}
void update(int id,int l,int r,int add)
{
    if(st[id].l == l && st[id].r == r)
    {
        st[id].add += add;
        PushUp(id);
        return;
    }
    if(st[id<<1].r >= r)
    {
        update(id<<1,l,r,add);
        PushUp(id);
        return;
    }
    if(st[id<<1|1].l <= l)
    {
        update(id<<1|1,l,r,add);
        PushUp(id);
        return;
    }
    update(id<<1,l,st[id<<1].r,add);
    update(id<<1|1,st[id<<1|1].l,r,add);
    PushUp(id);
}
int main()
{
    //freopen("in.txt","r",stdin);
    int n;
    while(scanf("%d",&n)!=EOF && n)
    {
        int k = 0,cnt = 0;
        for(int i = 1;i <= n;i++)
        {
            int x1,y1,x2,y2,x3,y3,x4,y4;
            scanf("%d%d%d%d%d%d%d%d",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4);
            X[k++] = x1;X[k++] = x2;X[k++] = x3;X[k++] = x4;
            if(x2 > x1 && y3 > y1)
            {
                edge[cnt].vis = 1;
                edge[cnt].x1 = x1;
                edge[cnt].x2 = x2;
                edge[cnt++].y = y1;
                edge[cnt].vis = -1;
                edge[cnt].x1 = x1;
                edge[cnt].x2 = x2;
                edge[cnt++].y = y3;
            }
            if(x3 > x1 && y2 > y1)
            {
                edge[cnt].vis = 1;
                edge[cnt].x1 = x1;
                edge[cnt].x2 = x3;
                edge[cnt++].y = y1;
                edge[cnt].vis = -1;
                edge[cnt].x1 = x1;
                edge[cnt].x2 = x3;
                edge[cnt++].y = y2;
            }
            if(x4 > x3 && y2 > y4)
            {
                edge[cnt].vis = 1;
                edge[cnt].x1 = x3;
                edge[cnt].x2 = x4;
                edge[cnt++].y = y4;
                edge[cnt].vis = -1;
                edge[cnt].x1 = x3;
                edge[cnt].x2 = x4;
                edge[cnt++].y = y2;
            }
            if(x2 > x4 && y2 > y3)
            {
                edge[cnt].vis = 1;
                edge[cnt].x1 = x4;
                edge[cnt].x2 = x2;
                edge[cnt++].y = y3;
                edge[cnt].vis = - 1;
                edge[cnt].x1 = x4;
                edge[cnt].x2 = x2;
                edge[cnt++].y = y2;
            }
        }
        sort(edge,edge+cnt,cmp);
        sort(X,X+k);
        buildtree(1,0,k-1);
        LL ans = 0,lastlen = 0,lasty = 0;
        for(int i = 0;i < cnt;i++)
        {
            ans += lastlen * (LL(edge[i].y) - lasty);
            int u = lower_bound(X,X+k,edge[i].x1)-X;
            int v = lower_bound(X,X+k,edge[i].x2)-X;
            update(1,u,v,edge[i].vis);
            lastlen = st[1].len;
            lasty = edge[i].y;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
题意:求长方体体积交
思路:离散Z轴,对每一个值,循环找到跨越这个层的长方体,添边排序,扫描线求面积交。对离散的Z逐层遍历,*下高度差,叠加就是体积交了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 2080
#define lson id<<1,l,mid
#define rson id<<1|1,mid,r
#define LL long long int
LL X[maxn],Z[maxn];
bool val[maxn];
struct ST
{
    int l,r,add,len1,len2,len3;
}st[maxn<<2];
struct REC
{
    LL x1,y1,x2,y2,z1,z2;
}rec[maxn];
struct Edge
{
    LL x1,x2,y,vis;
}edge[maxn];
bool cmp(Edge a,Edge b)
{
    return a.y < b.y;
}
void buildtree(int id,int l,int r)
{
    st[id].l = l,st[id].r = r,st[id].add = st[id].len1 = st[id].len2 = st[id].len3= 0;
    if(l + 1 == r)    return;
    int mid = (l+r) >> 1;
    buildtree(lson);
    buildtree(rson);
}
void PushUp(int id)
{
    if(st[id].add > 2)    st[id].len1 = st[id].len2 = st[id].len3 = X[st[id].r] - X[st[id].l];
    else if(st[id].add > 1)
    {
        st[id].len1 = st[id].len2 = X[st[id].r] - X[st[id].l];
        if(st[id].l + 1 == st[id].r)    st[id].len3 = 0;
        else st[id].len3 = st[id<<1].len1 + st[id<<1|1].len1;
    }
    else if(st[id].add > 0)
    {
        st[id].len1 = X[st[id].r] - X[st[id].l];
        if(st[id].l + 1 != st[id].r)    
        {
            st[id].len2 = st[id<<1].len1 + st[id<<1|1].len1;
            st[id].len3 = st[id<<1].len2 + st[id<<1|1].len2;
        }
        else st[id].len2 = st[id].len3 = 0;
    }
    else 
    {
        if(st[id].l + 1 != st[id].r)
        {
            st[id].len1 = st[id<<1].len1 + st[id<<1|1].len1;
            st[id].len2 = st[id<<1].len2 + st[id<<1|1].len2;
            st[id].len3 = st[id<<1].len3 + st[id<<1|1].len3;
        }
        else st[id].len1 = st[id].len2 = st[id].len3 = 0;
    }
}
void update(int id,int l,int r,int add)
{
    if(st[id].l == l && st[id].r == r)
    {
        st[id].add += add;
        PushUp(id);
        return;
    }
    if(st[id<<1].r >= r)
    {
        update(id<<1,l,r,add);
        PushUp(id);
        return;
    }
    if(st[id<<1|1].l <= l)
    {
        update(id<<1|1,l,r,add);
        PushUp(id);
        return;
    }
    update(id<<1,l,st[id<<1].r,add);
    update(id<<1|1,st[id<<1|1].l,r,add);
    PushUp(id);
}
int main()
{
    //freopen("in.txt","r",stdin);
    int t;    scanf("%d",&t);
    for(int cas = 1;cas <= t;cas++)
    {
        int n;    scanf("%d",&n);
        int kx = 0,kz = 0;
        memset(val,0,sizeof(val));
        for(int i = 1;i <= n;i++)
        {
            LL x1,y1,z1,x2,y2,z2;
            scanf("%I64d%I64d%I64d%I64d%I64d%I64d",&x1,&y1,&z1,&x2,&y2,&z2);
            X[kx++] = x1;
            X[kx++] = x2;
            if(!val[z1+520]){Z[kz++]  = z1;val[z1+520] = 1;}
            if(!val[z2+520]){Z[kz++] = z2;val[z2+520]=1;}
            rec[i].x1 = x1;
            rec[i].x2 = x2;
            rec[i].y1= y1;
            rec[i].y2 = y2;
            rec[i].z1 = z1;
            rec[i].z2 = z2;
        }
        sort(X,X+kx);
        sort(Z,Z+kz);
        //接下来是遍历每一层,筛选能覆盖该层的矩形,添边。
        //筛完之后边排序,然后就是矩形面积交的问题了、
        LL ans = 0,area = 0;
        for(int i = 0;i < kz;i++)
        {
            if(i)ans += area*(Z[i] - Z[i-1]);
            int zz = Z[i];int cnt = 0;
            for(int j = 1;j <= n;j++)
            {
                if(rec[j].z1 <= zz && rec[j].z2 > zz)
                {
                    edge[cnt].x1 = rec[j].x1;
                    edge[cnt].x2 = rec[j].x2;
                    edge[cnt].y = rec[j].y1;
                    edge[cnt++].vis = 1;
                    edge[cnt].x1 = rec[j].x1;
                    edge[cnt].x2 = rec[j].x2;
                    edge[cnt].y = rec[j].y2;
                    edge[cnt++].vis = -1;
                }
            }
            sort(edge,edge+cnt,cmp);
            buildtree(1,0,kx-1);
            area = 0;
            LL lastlen = 0,lasty = 0;
            for(int j = 0;j < cnt;j++)
            {
                area += lastlen*(edge[j].y - lasty);
                int u = lower_bound(X,X+kx,edge[j].x1) - X;
                int v = lower_bound(X,X+kx,edge[j].x2) - X;
                update(1,u,v,edge[j].vis);
                lastlen = st[1].len3;
                lasty = edge[j].y;
            }
        }
        printf("Case %d: %I64d\n",cas,ans);
    }
    return 0;
}
题意:平面上放着点,点有权,给一个矩形,求矩形最大能覆盖多少点权
思路:以每个星星为左下角画一个矩形,则只要我们所给的w*h矩形的右上角在刚才画的那个矩形内,就能覆盖到该星星。
           这样的话问题就转化为简单的矩形面积并的问题了。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 20080
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define LL long long int
LL X[maxn];
struct ST
{
	int l,r,key,add;
}st[maxn<<2];
struct Edge
{
	LL x1,x2,y,vis;
}edge[maxn];
bool cmp(Edge a,Edge b)
{
	if(a.y > b.y) return 0;
	if(a.y < b.y) return 1;
	if(a.y == b.y)
	{
		return a.vis >= b.vis;
	}
}
inline LL max(LL a,LL b)
{
	return a>b?a:b;
}
void PushDown(int id)
{
	if(st[id].add)
	{
		st[id<<1].add += st[id].add;
		st[id<<1|1].add += st[id].add;
		st[id<<1].key += st[id].add;
		st[id<<1|1].key += st[id].add;
		st[id].add = 0;
	}
}
void PushUp(int id)
{
	st[id].key = max(st[id<<1].key,st[id<<1|1].key);
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].key = st[id].add = 0;
	if(l == r)	return;
	int mid = (l + r)>>1;
	buildtree(lson);
	buildtree(rson);
}
void update(int id,int l,int r,int add)
{
	if(st[id].l == l && st[id].r == r)
	{
		st[id].add += add;
		st[id].key += add;
		return;
	}
	PushDown(id);
	if(st[id<<1].r >= r)
	{
		update(id<<1,l,r,add);
		PushUp(id);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		update(id<<1|1,l,r,add);
		PushUp(id);
		return;
	}
	update(id<<1,l,st[id<<1].r,add);
	update(id<<1|1,st[id<<1|1].l,r,add);
	PushUp(id);
}
int main()
{
	//freopen("in.txt","r",stdin);
	LL n,w,h;
	while(scanf("%lld%lld%lld",&n,&w,&h)==3)
	{
		int k = 0,cnt = 0;
		for(int i = 1;i <= n;i++)
		{
			LL x,y,add;
			scanf("%lld%lld%lld",&x,&y,&add);
			X[k++] = x;
			X[k++] = x+w-1;
			edge[cnt].x1 = x;
			edge[cnt].x2 = x+w-1;
			edge[cnt].y = y;
			edge[cnt++].vis = add;
			edge[cnt].x1 = x;
			edge[cnt].x2 = x+w-1;
			edge[cnt].y = y+h-1;
			edge[cnt++].vis = -add;
		}
		sort(X,X+k);
		sort(edge,edge+cnt,cmp);
		buildtree(1,0,k-1);
		int ans = 0;
		for(int i = 0;i < cnt;i++)
		{
			int u = lower_bound(X,X+k,edge[i].x1) - X;
			int v = lower_bound(X,X+k,edge[i].x2) - X;
			update(1,u,v,edge[i].vis);
			ans = max(ans,st[1].key);
		}
		printf("%d\n",ans);
	}
	return 0;
}
题意:平面上散放着一些点,A找个点纵向切一刀,B在A所切到的点中选一个横向切一刀。平面分成四块,A得分为一三象限的点数,B为二四象限。彼此都想拿最多分
思路:我的做法总体上是建2次树,一次从左往右扫描,得到以每个点为中心切割后二三象限分别的点数,,一次从右往左扫描。得到以每个点为中心切割后一四象限的点数。具体实现的时候要滞后update.因为线覆盖的点是无效的。所以比如有(3,4) (3,5) (4,5) (4,6)在X=3的时候,我们只执行query,在X=4的时候先执行X=3的update.然后再执行X=4的query.(这个代码写得好挫~)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
#define maxn 200080
#define inf 0x3f3f3f3f
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
int X[maxn],Y[maxn],n;
struct ST
{
	int l,r,sum;
}st[maxn<<2];
struct Point
{
	int x,y;
}ppoint[maxn];
int binary(int x,int y)
{
	int l = 1,r = n;
	while(l < r)
	{
		int mid = ( l + r ) >> 1;
		if(ppoint[mid].x > x || (ppoint[mid].x == x && ppoint[mid].y <=  y))	r = mid;
		else l = mid+1;
	}
	return l;
}
inline int max(int a,int b)
{
	return a>b?a:b;
}
bool cmp(Point a,Point b)
{
	if(a.x < b.x)	return 1;
	if(a.x > b.x)	return 0;
	if(a.x == b.x)	return a.y > b.y;
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].sum = 0;
	if(l == r)	return;
	int mid = (l + r) >> 1;
	buildtree(lson);
	buildtree(rson);
}
void PushUp(int id)
{
	st[id].sum = st[id<<1].sum + st[id<<1|1].sum;
}
void update(int id,int pos)
{
	if(st[id].l == pos && st[id].r == pos)
	{
		st[id].sum += 1;
		return;
	}
	if(st[id<<1].r >= pos)	update(id<<1,pos);
	else update(id<<1|1,pos);
	PushUp(id);
}
int query(int id,int l,int r)
{
	if(st[id].l == l && st[id].r == r)
	{
		return st[id].sum;
	}
	if(st[id<<1].r >= r)
	{
		return query(id<<1,l,r);
	}
	if(st[id<<1|1].l <= l)
	{
		return query(id<<1|1,l,r);
	}
	return query(id<<1,l,st[id<<1].r) + query(id<<1|1,st[id<<1|1].l,r);
}
vector <int> p[maxn];
vector <int> ans[maxn];
vector <int> final;
set <int> coll;
set <int>  :: iterator it;
int main()
{
	//freopen("in.txt","r",stdin);
	while(scanf("%d",&n)!=EOF && n)
	{
		int k = 0;
		for(int i = 1;i <= n;i++)
		{
			scanf("%d%d",&ppoint[i].x,&ppoint[i].y);
			X[k] = ppoint[i].x;	Y[k++] = ppoint[i].y;
		}
		sort(X,X+k);	sort(Y,Y+k);
		for(int i = 0;i < k;i++)	
		{
			p[i].clear();	ans[i].clear();
		}
		ans[n].clear();
		for(int i = 1;i <= n;i++)
		{
			int x = lower_bound(X,X+k,ppoint[i].x) - X;
			int y = lower_bound(Y,Y+k,ppoint[i].y) - Y;
			p[x].push_back(y);
			ppoint[i].x = x;
			ppoint[i].y = y;
		}
		sort(ppoint+1,ppoint+n+1,cmp);
		buildtree(1,0,k-1);
		int s = 1,t = 1,lastx = inf;
		for(int i = 1;i <= n;i++)
		{
			if(i!=1 && ppoint[i].x != lastx)
			{
				for(int j = s;j <= t;j++)
				{
					update(1,ppoint[j].y);
				}
				s = t = i;
			}
			t = i;	lastx = ppoint[i].x;
			int lt = ppoint[i].y < k-1?query(1,ppoint[i].y+1,k-1):0;
			int ld = ppoint[i].y>0?query(1,0,ppoint[i].y-1):0;
			ans[i].push_back(lt);
			ans[i].push_back(ld);
		}
		buildtree(1,0,k-1);
		s = t = n,lastx = inf;
		for(int i = n;i >= 1;i--)
		{
			if(i!=n && ppoint[i].x != lastx)
			{
				for(int j = s;j >= t;j--)
				{
					update(1,ppoint[j].y);
				}
				s = t = i;
			}
			t = i;	lastx = ppoint[i].x;
			int rt = ppoint[i].y < k-1?query(1,ppoint[i].y+1,k-1):0;
			int rd = ppoint[i].y>0?query(1,0,ppoint[i].y-1):0;
			ans[i].push_back(rt);
			ans[i].push_back(rd);
		}
		int pos = 1,Ans = -inf;
		for(int i = 0;i < k;i++)
		{
			int maxs = -1;
			for(int j = 0;j < p[i].size();j++)
			{
				int pnum = binary(i,p[i][j]);
				if(maxs == -1)	maxs = ans[pnum][1] + ans[pnum][2];
				if(ans[pnum][1] + ans[pnum][2] < maxs)
				{
					maxs = ans[pnum][1] + ans[pnum][2];
				}
			}
			if(maxs == Ans)
			{
				final.push_back(i);
			}
			if(maxs > Ans)
			{
				Ans = maxs;
				final.clear();
				final.push_back(i);
			}
		}
		printf("Stan: %d; ",Ans);
		printf("Ollie: ");
		coll.clear();
		for(int i = 0;i < final.size();i++)
		{
			int u = final[i];
			Ans = -1;
			for(int j = 0;j < p[u].size();j++)
			{
				int pnum = binary(u,p[u][j]);
				Ans = max(Ans,ans[pnum][0]+ans[pnum][3]);
			}
			coll.insert(Ans);
		}
		int cnt = 0,sum = coll.size();
		for(it = coll.begin();it != coll.end();it++)
		{
			printf("%d",*it);
			cnt++;
			if(cnt == sum)printf(";\n");
			else printf(" ");
		}
	}
	return 0;
}

HDU3255 Farming
题意:每个工人会在矩形区域种蔬菜,同快土地如果被多种蔬菜覆盖只算最贵的蔬菜。求这块土地能得到的最大价值
思路:巧妙的将蔬菜的价值抽象为高。将平面转化为空间,求长方体体积并~
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 60080
#define lson id<<1,l,mid
#define rson id<<1|1,mid,r
#define LL long long int
int X[maxn],Z[208];
struct ST
{
	int l,r,len,add;
}st[maxn<<2];
struct REC
{
	int x1,x2,y1,y2,z1,z2;
}rec[maxn];
struct Edge
{
	int x1,x2,y,vis;
}edge[maxn];
bool cmp(Edge a,Edge b)
{
	return a.y < b.y;
}
void PushUp(int id)
{
	if(st[id].add > 0)	st[id].len = X[st[id].r] - X[st[id].l];
	else if(st[id].l + 1 == st[id].r)	st[id].len = 0;
	else st[id].len = st[id<<1].len + st[id<<1|1].len;
}
void buildtree(int id,int l,int r)
{
	st[id].l = l,st[id].r = r,st[id].len = st[id].add = 0;
	if(l + 1 == r)	return;
	int mid = (l + r) >> 1;
	buildtree(lson);
	buildtree(rson);
}
void update(int id,int l,int r,int add)
{
	if(st[id].l == l && st[id].r == r)
	{
		st[id].add += add;
		PushUp(id);
		return;
	}
	if(st[id<<1].r >= r)
	{
		update(id<<1,l,r,add);
		PushUp(id);
		return;
	}
	if(st[id<<1|1].l <= l)
	{
		update(id<<1|1,l,r,add);
		PushUp(id);
		return;
	}
	update(id<<1,l,st[id<<1].r,add);
	update(id<<1|1,st[id<<1|1].l,r,add);
	PushUp(id);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int t;	scanf("%d",&t);
	for(int cas = 1;cas <= t;cas++)
	{
		int n,m;	scanf("%d%d",&n,&m);
		Z[0] = 0;
		for(int i = 1;i <= m;i++)	scanf("%d",&Z[i]);
		int k = 0;
		for(int i = 1;i <= n;i++)
		{
			int x1,y1,x2,y2,z;
			scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&z);
			rec[i].x1 = x1;
			rec[i].x2 = x2;
			rec[i].y1 = y1;
			rec[i].y2 = y2;
			rec[i].z1 = 0;
			rec[i].z2 = Z[z];
			X[k++] = x1;
			X[k++] = x2;
		}//矩形读入完毕
		sort(X,X+k);
		buildtree(1,0,k-1);
		sort(Z,Z+m+1);
		LL ans = 0,lastarea = 0;
		for(int i = 0;i <= m;i++)
		{
			if(i)	ans += lastarea*LL(Z[i] - Z[i-1]);
			int cnt = 0;
			for(int j = 1;j <= n;j++)
			{
				if(rec[j].z1 <= Z[i] && rec[j].z2 > Z[i])
				{
					int u = lower_bound(X,X+k,rec[j].x1) - X;
					int v = lower_bound(X,X+k,rec[j].x2) - X;
					edge[cnt].x1 = u;
					edge[cnt].x2 = v;
					edge[cnt].y = rec[j].y1;
					edge[cnt++].vis = 1;
					edge[cnt].x1 = u;
					edge[cnt].x2 = v;
					edge[cnt].y = rec[j].y2;
					edge[cnt++].vis = -1;
				}
			}
			sort(edge,edge+cnt,cmp);
			lastarea = 0;
			LL lastlen = 0,lasty = 0;
			for(int i = 0;i < cnt;i++)
			{
				if(i)	lastarea += lastlen*LL(edge[i].y - lasty);
				int u = edge[i].x1;
				int v = edge[i].x2;
				update(1,u,v,edge[i].vis);
				lastlen = st[1].len;
				lasty = edge[i].y;
			}
		}
		printf("Case %d: %I64d\n",cas,ans);
	}
	return 0;
}

未完待续!欢迎路过的犇犇多多赐教 微笑
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值