【专题属性】线段树

终于把线段树的AK了...满纸辛酸泪啊...

hint:

ST[i]表示结点编号为i的线段树结点。

ST[i*2],ST[i*2+1]分别为左右儿子。

结构体

struct node

{

int l,r; 代表i记录的是区间[l,r]

int lazy;   延迟标记。

...

} ST[maxn];

以上定义在下文中均使用。


Brackets(SPOJ_GSS1)

题目大意(很短就直接粘贴了):

You are given a sequence A[1], A[2], ..., A[N] . ( |A[i]| ≤ 15007 , 1 ≤ N ≤ 50000 ). A query is defined as follows:
Query(x,y) = Max { a[i]+a[i+1]+...+a[j] ; x ≤ i ≤ j ≤ y }.
Given M queries, your program must output the results of these queries.


题解:

线段树基础题。

ST[i].sum=ST[2*i].sum+ST[2*i+1].sum;
ST[i].lmax=MAX(ST[2*i].lmax,ST[2*i].sum+ST[2*i+1].lmax);
ST[i].rmax=MAX(ST[2*i].rmax+ST[2*i+1].sum,ST[2*i+1].rmax);
ST[i].max=MAX(ST[2*i+1].max,MAX(ST[2*i].max,ST[2*i].rmax+ST[2*i+1].lmax));


#include 
#define MAX(a,b) ((a)>(b)?(a):(b))
#define maxn 50005

struct STNode
{
	int l,r;
    long long sum,lmax,rmax,max;
};

STNode ST[maxn*4];
long long Node[maxn];


void build(int l,int r,int i)
{
	ST[i].l=l;
	ST[i].r=r;
	ST[i].sum=0;
	ST[i].max=ST[i].lmax=ST[i].rmax=0;
	
	if (l>1;
		build(l,mid,i*2);
		build(mid+1,r,i*2+1);
	}
}

void insert(int x,int i)
{
	if (ST[i].l==ST[i].r)
	{
		ST[i].sum=Node[x];
		ST[i].lmax=ST[i].rmax=ST[i].max=Node[x];
		return ;
	}
	int mid=(ST[i].l+ST[i].r)>>1;
	if (x<=mid)
	insert(x,2*i);else
	insert(x,2*i+1);
	ST[i].sum=ST[2*i].sum+ST[2*i+1].sum;
	ST[i].lmax=MAX(ST[2*i].lmax,ST[2*i].sum+ST[2*i+1].lmax);
	ST[i].rmax=MAX(ST[2*i].rmax+ST[2*i+1].sum,ST[2*i+1].rmax);
	ST[i].max=MAX(ST[2*i+1].max,MAX(ST[2*i].max,ST[2*i].rmax+ST[2*i+1].lmax));
}

STNode qwery(int l,int r,int i)
{
	if (ST[i].l==l && ST[i].r==r)
	return ST[i];
	int mid=(ST[i].l+ST[i].r)>>1;
	if (r<=mid)
	return qwery(l,r,2*i);else
	if (l>mid)
	return qwery(l,r,2*i+1);else
	{
        
		STNode ans1=qwery(l,mid,2*i);
		STNode ans2=qwery(mid+1,r,2*i+1);
		STNode ans;
		ans.lmax=MAX(ans1.sum+ans2.lmax,ans1.lmax);
		ans.rmax=MAX(ans1.rmax+ans2.sum,ans2.rmax);
		ans.max=MAX(ans2.max,MAX(ans1.max,ans1.rmax+ans2.lmax));
		ans.sum=ans1.sum+ans2.sum;
		return ans;
	}
}


int main()
{
	int m,n,l,r;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%lld",&Node[i]);
	
	build(1,n,1);
	for (int i=1;i<=n;i++)
	insert(i,1);
	
	scanf("%d",&m);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&l,&r);
		printf("%lld\n",qwery(l,r,1).max);
    }
  // while (1);	
	return 0;
}

/*
8
1 -2 3 -4 5 -6 7 -8
10
1 8
*/



Black and white (hdu_3911)

题目大意(还是很短,直接粘贴):

There are a bunch of stones on the beach; Stone color is white or black. Little Sheep has a magic brush, she can change the color of a continuous stone, black to white, white to black. Little Sheep like black very much, so she want to know the longest period of consecutive black stones in a range [i, j].


题解:

黑色的石头记为1,白色的记为-1,做法就与上题类似了,不过要加上lazy标记。(代码写的有点丑...)


#include 


#define maxn 200005
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))


struct STNode
{
	int l,r,sum,lmax,rmax,max,lmin,rmin,min;
	bool now;
};

STNode ST[maxn*4];
long Node[maxn];


void build(int l,int r,int i)
{
	ST[i].l=l;
	ST[i].r=r;
	ST[i].sum=0;
	ST[i].max=ST[i].lmax=ST[i].rmax=0;
	ST[i].min=ST[i].rmin=ST[i].lmin=0;
	ST[i].now=true;
	if (l>1;
		build(l,mid,i*2);
		build(mid+1,r,i*2+1);
	}
}

void insert(int x,int i)
{
	if (ST[i].l==ST[i].r)
	{
		ST[i].sum=Node[x];
		ST[i].lmax=ST[i].rmax=ST[i].max=Node[x];
		ST[i].lmin=ST[i].rmin=ST[i].min=Node[x];
		return ;
	}
	int mid=(ST[i].l+ST[i].r)>>1;
	if (x<=mid)
	insert(x,2*i);else
	insert(x,2*i+1);

	if (ST[2*i].sum==ST[2*i].r-ST[2*i].l+1  && ST[2*i+1].lmax>0)
	ST[i].lmax=ST[2*i].sum+ST[2*i+1].lmax;else
	ST[i].lmax=ST[2*i].lmax;
	
	if (ST[2*i+1].sum==ST[2*i+1].r-ST[2*i+1].l+1 && ST[2*i].rmax>0)
	ST[i].rmax=ST[2*i].rmax+ST[2*i+1].sum;else
	ST[i].rmax=ST[2*i+1].rmax;
	
	ST[i].max=MAX(ST[2*i].max,MAX(ST[2*i+1].max,ST[2*i].rmax+ST[2*i+1].lmax));
	
	if (ST[2*i].sum==ST[2*i].l-ST[2*i].r-1 && ST[2*i+1].lmin<0) 
	ST[i].lmin=ST[2*i].sum+ST[2*i+1].lmin;else
    ST[i].lmin=ST[2*i].lmin;
    
    if (ST[2*i+1].sum==ST[2*i+1].l-ST[2*i+1].r-1 && ST[2*i].rmin<0)
	ST[i].rmin=ST[2*i].rmin+ST[2*i+1].sum;else
    ST[i].rmin=ST[2*i+1].rmin;
    
	ST[i].min=MIN(ST[2*i].min,MIN(ST[2*i+1].min,ST[2*i].rmin+ST[2*i+1].lmin));			
	ST[i].sum=ST[2*i].sum+ST[2*i+1].sum;
}

void change(int l,int r,int i,bool flag)
{
	if (!ST[i].now) flag=!flag;
	//ST[i].now=flag;
	if (ST[i].l==l && ST[i].r==r)
	{
	//	ST[i].now=!flag;
		STNode ans=ST[i];
		if (ST[i].now!=!flag)
		{
		ST[i].lmin=-ans.lmax;
		ST[i].rmin=-ans.rmax;
		ST[i].min=-ans.max;
		ST[i].lmax=-ans.lmin;
		ST[i].rmax=-ans.rmin;
		ST[i].max=-ans.min;
		ST[i].sum=-ans.sum;
        }
        ST[i].now=!flag;
		return ;
	}
	ST[i].now=flag;
	int mid=(ST[i].l+ST[i].r)>>1;
	if (r<=mid)
	{

	       change(l,r,i*2,flag);
	        change(mid+1,ST[i].r,i*2+1,!flag);
	       ST[i].now=true;
    }else
	   if (l>mid)
         {
            change(ST[i].l,mid,i*2,!flag);
	        change(l,r,i*2+1,flag);
	        ST[i].now=true;
        }else
	{
		change(l,mid,i*2,flag);
		change(mid+1,r,i*2+1,flag);	
		ST[i].now=true;
	}
	if (ST[2*i].sum==ST[2*i].r-ST[2*i].l+1  && ST[2*i+1].lmax>0)
	ST[i].lmax=ST[2*i].sum+ST[2*i+1].lmax;else
	ST[i].lmax=ST[2*i].lmax;
	
	if (ST[2*i+1].sum==ST[2*i+1].r-ST[2*i+1].l+1 && ST[2*i].rmax>0)
	ST[i].rmax=ST[2*i].rmax+ST[2*i+1].sum;else
	ST[i].rmax=ST[2*i+1].rmax;
	
	ST[i].max=MAX(ST[2*i].max,MAX(ST[2*i+1].max,ST[2*i].rmax+ST[2*i+1].lmax));
	
	if (ST[2*i].sum==ST[2*i].l-ST[2*i].r-1 && ST[2*i+1].lmin<0) 
	ST[i].lmin=ST[2*i].sum+ST[2*i+1].lmin;else
    ST[i].lmin=ST[2*i].lmin;
    
    if (ST[2*i+1].sum==ST[2*i+1].l-ST[2*i+1].r-1 && ST[2*i].rmin<0)
	ST[i].rmin=ST[2*i].rmin+ST[2*i+1].sum;else
    ST[i].rmin=ST[2*i+1].rmin;
    
	ST[i].min=MIN(ST[2*i].min,MIN(ST[2*i+1].min,ST[2*i].rmin+ST[2*i+1].lmin));			
	ST[i].sum=ST[2*i].sum+ST[2*i+1].sum;
	
}

STNode qwery(int l,int r,int i,bool flag)
{
	STNode ans;

	if (!ST[i].now) flag=!flag;
	if (ST[i].l!=l || ST[i].r!=r)
	{
		int mid=(ST[i].l+ST[i].r)>>1;
		int t1=mid-ST[i].l+1;
		int t2=ST[i].r-mid;
		if (r<=mid)
			return qwery(l,r,2*i,flag);else
		if (l>mid)
			return qwery(l,r,2*i+1,flag);else
		{
        
			STNode ans1=qwery(l,mid,2*i,flag);
			STNode ans2=qwery(mid+1,r,2*i+1,flag);
		//	STNode ans;
		    
            
            if (ans1.sum ==t1 && ans2.lmax>0) 
			ans.lmax=ans1.sum+ans2.lmax;else 
            ans.lmax=ans1.lmax;
            if (ans2.sum==t2 && ans1.rmax>0)
			ans.rmax=ans1.rmax+ans2.sum;else
            ans.rmax=ans2.rmax;
			ans.max=MAX(ans2.max,MAX(ans1.max,ans1.rmax+ans2.lmax));
			if (ans1.sum==-t1 && ans2.lmin<0)
            ans.lmin=ans1.sum+ans2.lmin;else
            ans.lmin=ans1.lmin;
            if (ans2.sum==-t2 && ans1.rmin<0)
			ans.rmin=ans1.rmin+ans2.sum;else 
            ans.rmin=ans2.rmin;
			ans.min=MIN(ans2.min,MIN(ans1.min,ans1.rmin+ans2.lmin));			
			ans.sum=ans1.sum+ans2.sum;
			return ans;
		}
	} else 
       {
        ans=ST[i];
	    //ST[i].now=flag;
	    if (flag==ST[i].now) return ans;else
	   {
        //  ST[i].now=flag;
		  STNode ans1;
	   	   ans1.lmin=-ans.lmax;
		  ans1.rmin=-ans.rmax;
		  ans1.min=-ans.max;
		  ans1.lmax=-ans.lmin;
		  ans1.rmax=-ans.rmin;
		  ans1.max=-ans.min;
		  ans1.sum=-ans.sum;
         return ans1;
         }  
         return ans;}
}


int main()
{
	int m,n,l,r,p;
	while (scanf("%d",&n)!=EOF){
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&Node[i]);
		if (Node[i]==0)
		Node[i]=-1;
	}
	build(1,n,1);
	for (int i=1;i<=n;i++)
	insert(i,1);
	
	scanf("%d",&m);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&p,&l,&r);
		if (!p)
		{
            int ans=qwery(l,r,1,true).max;
            if (ans<0) ans=0;
	       	printf("%d\n",ans);
        } else
		  change(l,r,1,true);
    }
}
 //  while (1);	
	return 0;
}



Level up (hdu_3954)

题目大意(偷懒了,直接粘贴):

Level up is the task of all online games. It's very boooooooooring. There is only level up in those games, except level up.
In a online game, there are N heroes numbered id from 1 to N, each begins with level 1 and 0 Experience. They need to kill monsters to get Exp and level up.
There are many waves of monsters, each wave, the heroes with id from li to ri will come to kill monsters and those hero with level k will get ei*k Exp. If one hero's Exp reach Needk then the hero level up to level k immediately.
After some waves, I will query the maximum Exp from li to ri.
Now giving the information of each wave and Needk, please tell me the answer of my query.


题解:

这一题还是线段树维护+lazy标记。不过因为不同等级加上的exp是不同的,所以原本的lazy操作便行不通了。

1.经验系数:假设某个英雄当前等级为lv,距离下一个等级需要的经验为exp,那么经验系数就是exp/lv。(即若某个怪的ei>=经验系数,则会升级)

2.对于线段树上某个区间,维护区间内结点的最小经验系数min(如果ei>=min,说明区间内有英雄要升级)

3.如果对于某次插入,ei>=ST[i].min,说明区间内有英雄要升级,则继续往下做,否则将添加的ei记录在ST[i].lazy标记上,下次需要更新子节点的时候释放就好。

4.时间复杂度。因为k<=10,所以每个英雄最多升10级,所以升级操作加起来最多是O(N*K*Lg N).


#include 
#include 
#define INF 0xfffffff
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)<(b)?(a):(b)
#define LV 11
#define maxn 10005
using namespace std;

struct node
{
	int l,r,lazy,dis,lv,mexp;
}	ST[maxn*6];

int k,need[LV];

void build(int l,int r,int i)
{
	ST[i].lazy=0;
	ST[i].l=l;
	ST[i].r=r;
	ST[i].mexp=0;
	ST[i].lv=1;
	ST[i].dis=need[1];
	if (l==r)
	{
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,2*i);
	build(mid+1,r,2*i+1);
}

void insert(int l,int r,int exp,int i)
{
    if (ST[i].l==ST[i].r)
    {
        int u=ST[i].mexp+exp*ST[i].lv;
        int Lv=ST[i].lv;
        while (u>=need[Lv]) Lv++;
        ST[i].lv=Lv;
        ST[i].mexp=u;
        ST[i].dis=(need[Lv]-u)/Lv+((need[Lv]-u)%Lv!=0);
        return ;
    }
	if (ST[i].l==l && ST[i].r==r)
	{
		ST[i].lazy+=exp;
        if (ST[i].dis<=exp)
        {
            int mid=(l+r)>>1;
            insert(l,mid,ST[i].lazy,i*2);
            insert(mid+1,r,ST[i].lazy,i*2+1);
            ST[i].lazy=0;
            ST[i].mexp=MAX(ST[2*i].mexp,ST[2*i+1].mexp);
            ST[i].lv=MAX(ST[2*i].lv,ST[2*i+1].lv);
            ST[i].dis=MIN(ST[2*i].dis,ST[2*i+1].dis);
            return ;
        }
        ST[i].mexp=ST[i].mexp+exp*ST[i].lv;
        ST[i].dis-=exp;
        //ST[i].dis=(need[ST[i].lv]-ST[i].mexp)/ST[i].lv+((need[ST[i].lv]-ST[i].mexp)%ST[i].lv!=0);
	}  else
	{
		int mid=(ST[i].l+ST[i].r)>>1;
		int u=ST[i].lazy;

        insert(ST[i].l,mid,u,2*i);
        insert(mid+1,ST[i].r,u,2*i+1);
		ST[i].lazy=0;
		if (r<=mid)
			insert(l,r,exp,i*2);else
		if (l>mid)
			insert(l,r,exp,i*2+1);else
		{
			insert(l,mid,exp,i*2);
			insert(mid+1,r,exp,i*2+1);
		}
		ST[i].mexp=MAX(ST[2*i].mexp,ST[2*i+1].mexp);
		ST[i].lv=MAX(ST[2*i].lv,ST[2*i+1].lv);
		ST[i].dis=MIN(ST[2*i].dis,ST[2*i+1].dis);
	}
}

int query(int l,int r,int i)
{
	if (ST[i].l==l && ST[i].r==r)
	{
        return ST[i].mexp;
	} else
	{
		int mid=(ST[i].l+ST[i].r)>>1;
		int ans;
		if (r<=mid)
			ans=query(l,r,i*2);else
		if (l>mid)
			ans=query(l,r,i*2+1);else
		ans=MAX(query(l,mid,i*2),query(mid+1,r,i*2+1));
		return ans;
	}
}



int main()
{
    int tt;
	cin>>tt;
	for(int tc=1;tc<=tt;tc++)
	{
        printf("Case %d:\n",tc);
		int n,m;
		scanf("%d%d%d",&n,&k,&m);
		for (int i=1;i<=k-1;i++)
		scanf("%d",&need[i]);
		need[k]=INF;
		need[0]=0;

		build(1,n,1);

        while (m--)
        {
        char ch;
        scanf("%c",&ch);
		while (ch!='W' && ch!='Q') scanf("%c",&ch);
		if (ch=='W')
		{
		    int l,r,exp;
			scanf("%d %d %d",&l,&r,&exp);
			insert(l,r,exp,1);
		} else
		{
            int l,r;
			scanf("%d %d",&l,&r);
			insert(l,r,0,1);//将需要更新的lazy都更新了
			printf("%d\n",query(l,r,1));

		}
        }
        printf("\n");
	}
	return 0;
}



/*
2
5 6 100
1 2 3 4 5
W 1 5 5
W 1 1 2
W 1 4 1
W 2 2 1
W 1 4 1
Q 2 4



*/



Can you answer these queries(hdu_4027)

题目大意:

A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the battleships. Each of the battleships can be marked a value of endurance. For every attack of our secret weapon, it could decrease the endurance of a consecutive part of battleships by make their endurance to the square root of it original value of endurance. During the series of attack of our secret weapon, the commander wants to evaluate the effect of the weapon, so he asks you for help.
You are asked to answer the queries that the sum of the endurance of a consecutive part of the battleship line.

Notice that the square root operation should be rounded down to integer.


题解:

考虑到数的数据范围<=2^63,则最多开方7次,num=1。若线段树区间内的点都为1,则不需要再进行开方。因此,只需要用一个ST[i].dis记录线段树结点i所代表的区间里的数做多还能开方几次(到1时则不能再开方),若ST[i].dis=0,则不需要继续往下维护。

时间复杂度:因为每个点最多开方7次,因此总操作O(N*7*Lg N)


hint:

充满怨念啊,这一题Debug了很久,试了很多大数据,还是不对,最后把printf("%lld\n",query(l,r,1));改成printf("%I64d\n",query(l,r,1));就AC了...


#include 
#include 
#include 
#define MAX(a,b) (a)>(b)?(a):(b)
#define maxn 100005
using namespace std;

struct node
{
    int l,r,dis;
    long long now;
}   ST[4*maxn];

int t[maxn];
long long a[maxn];

void build(int l,int r,int i)
{
    ST[i].l=l;
    ST[i].r=r;
   // ST[i].lazy=0;
    if (l==r)
    {
        ST[i].dis=t[l];
        ST[i].now=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,i*2);
    build(mid+1,r,i*2+1);
    ST[i].dis=MAX(ST[2*i].dis,ST[2*i+1].dis);
    ST[i].now=ST[2*i].now+ST[2*i+1].now;
}

void insert(int l,int r,int i)
{
    if (ST[i].l==ST[i].r)
    {
        if (ST[i].dis==0) return ;
        ST[i].dis--;
        ST[i].now=(long long) sqrt((double)ST[i].now);
        return ;
    }
    if (ST[i].l==l && ST[i].r==r)
    {
        if (ST[i].dis==0) return ;
        int mid=(l+r)>>1;
        insert(l,mid,i*2);
        insert(mid+1,r,i*2+1);
        ST[i].dis=MAX(ST[2*i].dis,ST[2*i+1].dis);
        ST[i].now=ST[2*i].now+ST[2*i+1].now;
    } else
    {
        int mid=(ST[i].l+ST[i].r)>>1;
        if (r<=mid)
        insert(l,r,i*2);else
        if (l>mid)
        insert(l,r,i*2+1);else
        {
            insert(l,mid,i*2);
            insert(mid+1,r,i*2+1);
        }
        ST[i].dis=MAX(ST[2*i].dis,ST[2*i+1].dis);
        ST[i].now=ST[2*i].now+ST[2*i+1].now;
    }
}

long long query(int l,int r,int i)
{
    if (ST[i].l==l && ST[i].r==r)
    return ST[i].now;
    int mid=(ST[i].l+ST[i].r)>>1;
    if (r<=mid)
    return query(l,r,2*i);else
    if (l>mid)
    return query(l,r,2*i+1);else
    return query(l,mid,2*i)+query(mid+1,r,2*i+1);
}


int main()
{
    int tt=0,n;
    while (scanf("%d",&n)!=EOF)
    {
        tt++;
        memset(ST,0,sizeof(ST));
        memset(a,0,sizeof(a));
        memset(t,0,sizeof(t));
        printf("Case #%d:\n",tt);
        for (int i=1;i<=n;i++)
        {
            scanf("%I64d",&a[i]);
            long long x=a[i];
            int tot=0;
            while (x>1)
            {
                tot++;
                x=(long long )sqrt((double)x);
            }
            t[i]=tot;
        }

        build(1,n,1);

        int m;
        scanf("%d",&m);
        for (int i=1;i<=m;i++)
        {
            int p,l,r;
            scanf("%d%d%d",&p,&l,&r);
            if (l>r) {int temp=l;l=r;r=temp;}
            if (p)
            {
                printf("%I64d\n",query(l,r,1));
            } else
                insert(l,r,1);
        }
        printf("\n");
    }
    return 0;
}


/*
10
23432532523 34324235234
3423423
32432423
342342343
2353265436
352353463
23532543523
23523543534
543543
100
0 1 10
1 1 10

*/



Brackets (SPOJ_BRCKTS)

题目大意:

你一串序列,只包含 '(' 和')',有两种操作,操作1:输入0,询问序列是否合法。操作2:输入x,将序列的第x个数取opposite one。


题解:

1.合法性。令左括号为1,右括号为-1,对于序列的任一前缀序列,都满足序列和>=0,且原序列的序列和=0,则合法。

2.易发现前缀序列都存在于线段树每一层的最左结点,即ST[1],ST[2],ST[4]....因此,只要记录ST[i].lmax即最大前缀和即可。若ST[1],ST[2]...的lmax都满足>=0,则合法。

ST[i].lmin=MIN(ST[2*i].lmin,ST[2*i].sum+ST[2*i+1].lmin);


#include 
#include 
#define MIN(a,b) (a)<(b)?(a):(b)
#define maxn 30005
using namespace std;

char str[maxn];

struct node
{
    int l,r,sum,lmin;
}   ST[4*maxn];

void build(int l,int r,int i)
{
    ST[i].l=l;
    ST[i].r=r;
    if (l==r)
    {
        int p;
        if (str[l-1]=='(')
        p=1;else p=-1;
        ST[i].sum=p;
        ST[i].lmin=p;
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,i*2);
    build(mid+1,r,i*2+1);
    ST[i].sum=ST[2*i].sum+ST[2*i+1].sum;
    ST[i].lmin=MIN(ST[2*i].lmin,ST[2*i].sum+ST[2*i+1].lmin);
}


void change(int x,int i)
{
    if (ST[i].l==ST[i].r)
    {
        ST[i].sum=-ST[i].sum;
        ST[i].lmin=-ST[i].lmin;
        return ;
    }
    int mid=(ST[i].l+ST[i].r)>>1;
    if (x<=mid) change(x,2*i);else
    change(x,2*i+1);
    ST[i].sum=ST[2*i].sum+ST[2*i+1].sum;
    ST[i].lmin=MIN(ST[2*i].lmin,ST[2*i].sum+ST[2*i+1].lmin);
}

int judge(int i)
{
    if (ST[i].l==ST[i].r) return (ST[i].lmin>=0);else
    return (ST[i].lmin>=0 && judge(2*i));
}


int main()
{
    int tt=0;

    while ((++tt)<=10)
    {
        printf("Test %d:\n",tt);
        int n,m;
        scanf("%d",&n);
        scanf("%s",&str);
        scanf("%d",&m);

        build(1,n,1);
        for (int i=1;i<=m;i++)
        {
            int x;
            scanf("%d",&x);
            if (!x)
            {
                if (ST[1].sum==0 && judge(1)) printf("YES\n");else
                printf("NO\n");
            } else
                change(x,1);
        }
    }

    return 0;
}

hint:

难得有一题一次AC...


Hit the Target!(ZOJ_3597)

题目大意:

Alice and Bob are playing a game on shooting. In this game, there are N guns and M targets. Each gun has only one bullet and can shoot at only one target. What's more, they are located in different places so that they can hit different targets. In this game, the guns appear one by one and disappear before the next gun appears, and so will the targets. From the N guns, Alice will choose P consecutive guns in equivalent possibility. Then Bob can choose Q consecutive targets so that he can shoot as many hits as possible. Now you are asked to calculate the expected hits Bob can get.


题解:

对于读入的Gi,Ti(表示枪Ti能打到靶Gi),抽象成坐标系上的点(Gi,Ti),权值为1,同时放入一个点(Gi,Ti+Q),权值为-1.

则可以把问题转换成:

用一个 (P-1)*(Q-1)的矩阵 [(P-1)*(Q-1)的矩阵最多能覆盖横坐标上连续的P个整数点,纵坐标上连续的Q个整点]  进行覆盖,求所覆盖的点的权值和的最大值,就是线段树的一类经典问题了。

特殊处理:

这一题要求一把枪最多只能打一个靶,所以对于横坐标为X的点只能取其中一个。

处理方法:(见代码)


#include 
#include 
#include 
#include 
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
#define maxn 200005
using namespace std;

struct Line
{
	int x,y,c,now;
	bool operator <(Line o) const
	{
        if (x!=o.x) 
		return x>1;
		build(l,mid,2*i);
		build(mid+1,r,2*i+1);
	}
}

void insert(int i,int x,int c)
{
	if (ST[i].l==ST[i].r)
	{
		ST[i].sum+=c;
		ST[i].maxs=ST[i].sum;
		return ;
	}
	int mid=(ST[i].l+ST[i].r)>>1;
	if (x<=mid)
	insert(2*i,x,c);else
	insert(2*i+1,x,c);
	ST[i].sum=ST[2*i].sum+ST[2*i+1].sum;
	ST[i].maxs=MAX(ST[2*i].maxs,ST[2*i].sum+ST[2*i+1].maxs);
}

void del(int i,int x,int c)
{
	if (ST[i].l==ST[i].r)
	{
		ST[i].sum-=c;
		ST[i].maxs=ST[i].sum;
		return ;
	}
	int mid=(ST[i].l+ST[i].r)>>1;
	if (x<=mid)
	del(2*i,x,c);else
	del(2*i+1,x,c);
	ST[i].sum=ST[2*i].sum+ST[2*i+1].sum;
	ST[i].maxs=MAX(ST[2*i].maxs,ST[2*i].sum+ST[2*i+1].maxs);
}

int GetIndex(int x)
{
	return lower_bound(Index,Index+cnt,x)-Index;
}

int main()
{
    int n,m,w,h,k,tt,lsum;
    int x1,y1,c1;
    scanf("%d",&tt);
	while (tt--)
	{
        scanf("%d%d%d%d",&n,&m,&w,&h);
        w--;h--;
        scanf("%d",&k);
        memset(Node,0,sizeof(Node));
        memset(Index,0,sizeof(Index));
		for (int i=0;i=w+1)
				{
                    while (i>0 && k-Node[now].x>w)
			         {
		  	         	del(1,GetIndex(Node[now].y),Node[now].c);
				        now++;
			         }
			        //if (i==2*k-1)
			        //ans+=ST[1].maxs;else
				    ans+=ST[1].maxs;
				  // lsum=ST[1].maxs;
				   // pos=0;
                }
				//i++;
            }
		//	ans+=ST[1].maxs;
		printf("%.2lf\n",ans*1.0/(n-w));
	}
	//while (1);
	return 0;
}


hint:

类似的一道题:POJ_2482



你可能感兴趣的:(总结)