HDU 1542(矩形面积并)

Atlantis

Problem Description
已知Atlantis的地图由许多矩形构成,求它们的面积并。
 


Input
题目有多组数据,每组数据的开头有一个整数n(1<=n<=100),表示地图数,接下来n行,每行4个小数 x1;y1;x2;y2 (0<=x1<x2<=100000;0<=y1<y2<=100000), 表示这张地图左上角坐标 (x1; y1)和右上角坐标 (x2;y2).
数据以0结束
 


Output
对于每组数据,你需要输出一行 “Test case #k”,k表示第k组数据(从1开始).第二行为“Total explored area: a”,a为面积并(保留2位小数),
每组数据后,请输出一个回车。
 


Sample Input
2 10 10 20 20 15 15 25 25.5 0
 


Sample Output
Test case #1 Total explored area: 180.00
 

矩形面积并入门题,

首先把直线拆成上边和下边,按高度排序,

横坐标离散化,建立线段树。

cnt[]表示某条线段被覆盖的次数,sum[]表示一条线段被覆盖的长度

我们在计算cnt[]时,没修改1个cnt[],就用pushup把sum[]更新,得到程序1:



#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
#include<map>
using namespace std;
#define MAXN (2000+10)
#define MAXT (1000*2*10)
#define Lson (x<<1)
#define Rson ((x<<1)^1)
int tt,n,size;
double x[MAXN];
struct SegMent
{
    double x1,x2,h;
    int type;
    SegMent(){}
    SegMent(double _x1,double _x2,double _h,int _type):x1(_x1),x2(_x2),h(_h),type(_type){}
    friend bool operator<(const SegMent a,const SegMent b){return a.h<b.h;    }
}a[MAXN];
map<double , int>  hpos;
double len[MAXT];
struct SegMentTree
{
    int n,M,cnt[MAXT];
    double sum[MAXT];
    int mark[MAXT];
    void fillchar(int _n)
    {
        n=_n;
        memset(cnt,0,sizeof(cnt));
        memset(sum,0,sizeof(sum));
        memset(mark,0,sizeof(mark));
        M=1;while (M-2<n) M<<=1;
        x[0]=0;memset(len,0.0,sizeof(len));
        for (int i=M+1;i<=M+size;i++) len[i]=x[i-M]-x[i-M-1];
        for (int i=M-1;i>0;i--) len[i]=len[i<<1]+len[(i<<1)^1];
    }
    void pushdown(int x)
    {
        if (x>>1) pushdown(x>>1);
        if (mark[x])
        {
            mark[Lson]+=mark[x];
            cnt[Lson]+=mark[x];
            mark[Rson]+=mark[x];
            cnt[Rson]+=mark[x];
            mark[x]=0;
        }
    }
    void pushup(int x)
    {
    //    pushdown(x);cout<<mark[5];
    //    cout<<x<<' ';

        sum[x]=cnt[x]?len[x]:(x>=M?0:sum[Lson]+sum[Rson]);
    }
    void update(int x)
    {
        for (;x;x>>=1)
        {
        //    cout<<"push "<<x<<endl;
            pushup(x);
        }
    }

    void insert(int l,int r,int c)
    {
        int ll=l-1+M,rr=r+1+M;
        for (l=l-1+M,r=r+1+M,pushdown(l>>1),pushdown(r>>1);l^r^1;l>>=1,r>>=1)
        {
            if (~l&1) {cnt[l+1]+=c;update(l+1);/*mark[l+1]+=c;*/} /*改变sum的值 */
            if (r&1) {cnt[r-1]+=c;update(r-1);/* mark[r-1]+=c;*/}
        }
//        update(ll);update(rr); //向上传递
    }
    void print()
    {
        cout<<"cnt ";
        for (int i=1;i<=M*2;i++) if (cnt[i]) cout<<i<<':'<<cnt[i]<<' ';
        cout<<endl;
        cout<<"sum ";
        for (int i=1;i<=M*2;i++) if (sum[i]) cout<<i<<':'<<sum[i]<<' ';
        cout<<endl;
        cout<<"Mark ";
        for (int i=1;i<=M*2;i++) if (mark[i]) cout<<i<<':'<<mark[i]<<' ';
        cout<<endl;
    }
}t;

int main()
{
//    freopen("Hdu1542.in","r",stdin);
    tt=0;
    while (scanf("%d",&n)!=EOF)
    {
        tt++;
        if (!n) break;
        for (int i=1;i<=2*n;i+=2)
        {
            scanf("%lf%lf%lf%lf",&a[i].x1,&a[i].h,&a[i].x2,&a[i+1].h);
            a[i+1].x1=a[i].x1; a[i+1].x2=a[i].x2;
            a[i].type=1;a[i+1].type=-1;
            x[i]=a[i].x1;x[i+1]=a[i].x2;
        }
        sort(a+1,a+2*n+1);
//        for (int i=1;i<=2*n;i++) cout<<a[i].x1<<' '<<a[i].x2<<' '<<a[i].h<<' '<<a[i].type<<endl;

        sort(x+1,x+2*n+1);
        size=unique(x+1,x+2*n+1)-(x+1);
//        for (int i=1;i<=size;i++) cout<<x[i]<<' ';cout<<endl;

        for (int i=1;i<=size;i++) hpos[x[i]]=i;

        t.fillchar(size);
/*        for (int i=1;i<=t.M*2;i++) cout<<len[i]<<' ';
        cout<<endl;
*/        /*
        t.insert(1,3,1);t.print();
        t.insert(2,4,2);
        cout<<t.find(1,3);
        cout<<endl;
        t.print();
        */
    //    t.insert(2,3,1);
        /*
        t.insert(2,3,1);
        t.print();
        t.insert(3,4,1);
        t.print();
        t.insert(2,3,-1);
        t.print();

        */

        double ans=0;a[0].h=a[1].h;
        for (int i=1;i<=2*n;i++)
        {
            ans+=(a[i].h-a[i-1].h)*t.sum[1];
            t.insert(hpos[a[i].x1]+1,hpos[a[i].x2],a[i].type);
/*            t.print();
            cout<<hpos[a[i].x1]+1<<' '<<hpos[a[i].x2]<<endl;
            cout<<t.sum[1]<<endl;
            cout<<ans<<endl;
*/        }
        printf("Test case #%dnTotal explored area: %.2lfnn",tt,ans);


    }
    return 0;
}

上述程序效率较慢(m(logm)^2) ,m为边数


我们考虑到,每次上传实在太耗时了。

仔细观察方程,发现每次修改的点一定是从叶结点到根节点的路上的结点的兄弟(不包括路上)

因此每次被影响的一定是路上的结点。

故我们每次只更新结点的值,最后直接由根节点向上递归,得到程序2-效率为O(mlogm) :


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
#include<map>
using namespace std;
#define MAXN (2000+10)
#define MAXT (1000*2*10)
#define Lson (x<<1)
#define Rson ((x<<1)^1)
int tt,n,size;
double x[MAXN];
struct SegMent
{
    double x1,x2,h;
    int type;
    SegMent(){}
    SegMent(double _x1,double _x2,double _h,int _type):x1(_x1),x2(_x2),h(_h),type(_type){}
    friend bool operator<(const SegMent a,const SegMent b){return a.h<b.h;    }
}a[MAXN];
map<double , int>  hpos;
double len[MAXT];
struct SegMentTree
{
    int n,M,cnt[MAXT];
    double sum[MAXT];
    int mark[MAXT];
    void fillchar(int _n)
    {
        n=_n;
        memset(cnt,0,sizeof(cnt));
        memset(sum,0,sizeof(sum));
        memset(mark,0,sizeof(mark));
        M=1;while (M-2<n) M<<=1;
        x[0]=0;memset(len,0.0,sizeof(len));
        for (int i=M+1;i<=M+size;i++) len[i]=x[i-M]-x[i-M-1];
        for (int i=M-1;i>0;i--) len[i]=len[i<<1]+len[(i<<1)^1];
    }
    void pushup(int x)
    {
        sum[x]=cnt[x]?len[x]:(x>=M?0:sum[Lson]+sum[Rson]);
    }
    void update(int x)
    {
        for (;x;x>>=1)
        {
            pushup(x);
        }
    }

    void insert(int l,int r,int c)
    {
        int ll=l-1+M,rr=r+1+M;
        for (l=l-1+M,r=r+1+M;l^r^1;l>>=1,r>>=1)
        {
            if (~l&1) {cnt[l+1]+=c;pushup(l+1);} /*改变sum的值 */
            if (r&1) {cnt[r-1]+=c;pushup(r-1);}
        }
        update(ll);update(rr);
    }
}t;

int main()
{
//    freopen("Hdu1542.in","r",stdin);
    tt=0;
    while (scanf("%d",&n)!=EOF)
    {
        tt++;
        if (!n) break;
        for (int i=1;i<=2*n;i+=2)
        {
			double x1,y1,x2,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            x[i]=x1;x[i+1]=x2;
            a[i]=SegMent(x1,x2,y1,1);
            a[i+1]=SegMent(x1,x2,y2,-1);
        }

        sort(a+1,a+2*n+1);
        sort(x+1,x+2*n+1);
        size=unique(x+1,x+2*n+1)-(x+1);
        for (int i=1;i<=size;i++) hpos[x[i]]=i;
        t.fillchar(size);


        double ans=0;a[0].h=a[1].h;
        for (int i=1;i<=2*n;i++)
        {
            ans+=(a[i].h-a[i-1].h)*t.sum[1];
            t.insert(hpos[a[i].x1]+1,hpos[a[i].x2],a[i].type);
        }
        printf("Test case #%dnTotal explored area: %.2lfnn",tt,ans);


    }
    return 0;
}





POJ 3171(区间覆盖最小代价)

Language:
Cleaning Shifts
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 2093   Accepted: 735

Description

有N (1 <= N <= 10,000)个区间,求覆盖[M,E](0 <= M <= E <= 86,399)的最小代价.
每个区间的代价为S (where 0 <= S <= 500,000).

Input

第一行3个整数: N, M, E. 

第二行到第n+1行,每行3个数,分别表示第i-1个区间的左端点T1,右端点T2,和代价S.

Output

仅一行表示最小代价,无解输-1.

Sample Input

3 0 4
0 2 3
3 4 2
0 0 1

Sample Output

5

Hint

样例解释
取第一个和第二个区间。

Source

这题是一个Dp问题,先列出Dp方程。
F[i]表示取[M,i]这个区间的代价
显然F[M-1]=0,答案就是F[E]
则方程为F[a[i].T2]=min(F[j])+a[i].S (T1-1<=J<=T2-1)
a[i]按T2从小到大排列;
那么显然a[i]取时,[M,T1-1]已经被前面的给取了,
因为如果被后面的[t1,t2] 取了,那么必有t1<T1 T2<t2, 就没必要取[T1,T2]了。

取最小的数可以用线段树做O(NlogN)。


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (10000+10)
#define MAXE (86399)
#define MAXS (500000+10)
#define INF (9187201950435737471)
int n,s,e;
struct SegMent
{
	int l,r;
	long long S;
	SegMent(){}
	SegMent(int _l,int _r,long long _S):l(_l),r(_r),S(_S){}
	friend bool operator<(const SegMent a,const SegMent b){return a.r<b.r;}
}a[MAXN];
struct SegMentTree //min()
{
	int n,M;
	long long t[MAXE*10];
	void fillchar(int _n)
	{
		n=_n+2;
		M=1;while (M-2<n) M<<=1;
		memset(t,127,sizeof(t));
	}
	void update(int x)
	{
		for (x>>=1;x;x>>=1) t[x]=min(t[x<<1],t[(x<<1)^1]);
	}
	void insert(int x,long long c)
	{
		x=x+2;
		x+=M;
		if (t[x]>c) {t[x]=c; update(x);	}
	}
	long long find(int l,int r)
	{
		l=l+2;r=r+2;

		l=l-1+M;r=r+1+M;
		long long ans=INF;
		while (l^r^1)
		{
			if (~l&1) ans=min(ans,t[l+1]);
			if (r&1) ans=min(ans,t[r-1]);
			l>>=1;r>>=1;
		}
		return ans;
	}
}t;
int main()
{
//	freopen("poj3171.in","r",stdin);
	scanf("%d%d%d",&n,&s,&e);
	for (int i=1;i<=n;i++) scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].S);
	sort(a+1,a+1+n);
	t.fillchar(e);
	t.insert(s-1,0);
	for (int i=1;i<=n;i++)
	{
		if (a[i].r<s) continue;
		t.insert(a[i].r,t.find(max(s-1,a[i].l-1),a[i].r-1)+a[i].S);
	}
/*	for (int i=t.M;i<=t.M*2;i++) cout<<t.t[i]<<' ';
	cout<<endl;
*/	if (t.t[e+2+t.M]==INF) cout<<"-1n";
	else cout<<t.t[e+2+t.M]<<endl;


	return 0;
}

POJ 2355(区间最大值-zkw线段树优化Dp方程)

Language:
Railway tickets
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 2379   Accepted: 823

Description

从1号车站Ekaterinburg到n号车站Sverdlovsk有一条铁路。 


订票时票价如下

2车站间的距离 -X

价格t

0<X<=L1

C1

L1<X<=L2

C2

L2<X<=L3

C3



由于总总原因,距离超过L3的车票不能定。
现在你打算从s号车站乘铁路到t号车站,求最小费用。

Input

第一行有6个数 L1, L2, L3, C1, C2, C3 (1 <= L1 < L2 < L3 <= 10^9, 1 <= C1 < C2 < C3 <= 10^9)
第二行为车站数N (2 <= N <= 10000)
第三行为s和t,表示起点和终点.
接下来n-1行,表示从第1好车站到第i(1<i<=n)号车站的距离(保证是升序).

Output

仅一行,为最小费用(<10^9)

Sample Input

3 6 8 20 30 40
7
2 6
3
7
8
13
15
23

Sample Output

70

Source

这题就是Dp

显然F[i]=F[j]+c[k](j<i,a[j]-a[i]<=l[k],1<=k<=3) F[i]表示从起点s到车站i的最小费用。 

用显然j单调递增,可用指针向后推(O(n))

注意判断INF为无解情况,可能超过变成负数,线段树记得开大


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (20000+10)
#define INF (2139062143)
int l[4],c[4],n,a[MAXN],st,en;
struct SegMentTree
{
	int t[MAXN*10],n,M;
	SegMentTree(){}
	SegMentTree(int _n):n(_n)
	{
		M=1;while (M-2<n) M<<=1;
		memset(t,127,sizeof(t));
	}
	void update(int x)
	{
		for (x>>=1;x;x>>=1) t[x]=min(t[x<<1],t[(x<<1)^1]);
	}
	void insert(int x,int c)
	{
		x+=M;
		if (t[x]>c)
		{
			t[x]=c;//cout<<endl<<'I'<<x<<' '<<c<<endl;
			update(x);
		}
	}
	int find(int l,int r)
	{
		if (l>r) return INF;
		l=l-1+M;r=r+1+M;
		int ans=INF;
		while (l^r^1)
		{
			if (~l&1) ans=min(ans,t[l+1]);
			if (r&1) ans=min(ans,t[r-1]);
			l>>=1;r>>=1;
		}
		return ans;
	}
}t;
void push(int &j,int L,int i)
{
	while (a[i]-a[j]>L) j++;
}
int main()
{
	scanf("%d%d%d%d%d%d%d",&l[1],&l[2],&l[3],&c[1],&c[2],&c[3],&n);
	t=SegMentTree(n);
	scanf("%d%d",&st,&en);if (st>en) swap(st,en);
	if (st==en) {cout<<"0"<<endl;return 0;	}
	a[1]=0;
	for (int i=2;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	int j[4]={st,st,st,st};
	t.insert(st,0);
	for (int i=st+1;i<=en;i++)
	{
		int value=INF;
		for (int k=1;k<=3;k++)
		{
			push(j[k],l[k],i);
//			cout<<j[k]<<' ';
			int p=t.find(j[k],i-1);
			if (p<INF) p+=c[k];
			value=min(value,p);
		}
//		cout<<value<<' '<<endl;
		t.insert(i,value);
//		for (int i=1;i<=t.M*2;i++) if (t.t[i]-INF) cout<<i<<' '<<t.t[i]<<' ';
//		cout<<endl;
	}
	cout<<t.find(en,en)<<endl;
	return 0;
}

POJ 3298(用unique离散化优化zkw线段树)

Language:
Antimonotonicity
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 2753   Accepted: 1175

Description

Mary数列是指在一个长度为n的序列(元素大小不超过n)中找到如下的子序列:

Mary0 > Mary1 < Mary2 > Mary3 < ...

请求出它的最长序列大小。

Input

第一行为数据数 T ≤ 50

接下来T行,每行第一个数n( 30000)表示原序列大小,接下来n个数为给定序列

Output

对每组数据输出一行为Mary数列最长长度。

Sample Input

4
5 1 2 3 4 5
5 5 4 3 2 1
5 5 1 4 2 3
5 2 4 1 3 5

Sample Output

1
2
5
3

Source

这题和LIS有异曲同工之妙,都是在给定区间找最大值

显然可以建立两棵线段树(并互相传递值),表示(...?<a[i]) 中使得“..”长度最长的大小,

由于用Unique(指针头,指针尾+1)离散了序列,用-INF和INF表示边界(特别注意离散Hash-map<int,int> Hpos一定要开在Struct外,否则反复建会超时(平衡树用来干这个……)

于是t.t[i][j] 表示第ith线段树的端点值。

i=0表示(1,3,5... 即除1外前面跟了<号的)的数

i=1表示(2,4,6... 即前面跟>的)数


于是本题转化为维护(..?<)的最长长度。

同理建立(..?>),注意特判序列开头那个数(第二个序列的长度必须超过1,表示其并非开头)


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
#include<iostream>
#include<map>
using namespace std;
#define MAXN (30000+10)
#define INF (2139062143)
#define NDEBUG
map<int,int> hpos;
struct SegMentTree  //t[0]->'?<' t[1]->'?>'
{
	int n,M,t[2][MAXN*5],a[MAXN],a_sort[MAXN],size;
	SegMentTree(){}
	SegMentTree(int _n):n(_n)
	{
		M=1;while (M-2<n) M<<=1;
		memset(t,0,sizeof(t));
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		memcpy(a_sort,a,sizeof(a));
		sort(a_sort+1,a_sort+n+1);
		#ifndef NDEBUG
		for (int i=1;i<=n;i++) cout<<a_sort[i]<<' ';
		cout<<endl;
		#endif
		size=unique(a_sort+1,a_sort+n+1)-(a_sort+1);
		#ifndef NDEBUG
		for (int i=1;i<=size;i++) cout<<a_sort[i]<<' ';
		cout<<endl;
		cout<<size;
		#endif
		for (int i=1;i<=size;i++) hpos[a_sort[i]]=i;
		hpos[-INF]=0;hpos[INF]=size+1;
	}
	void update(int x,int type)
	{
		for (x>>=1;x;x>>=1) t[type][x]=max(t[type][x<<1],t[type][(x<<1)^1]);
	}
	void insert(int x,int c,int type)
	{
		x=hpos[x]+M;
		if (t[type][x]<c)
		{
			t[type][x]=c;
			update(x,type);
		}
	}
	int find(int l,int r,int type)
	{
		l=hpos[l];r=hpos[r];
//		if (type) l++; else r--;
		#ifndef NDEBUG
		cout<<l<<' '<<r<<';'<<endl;
		#endif

		l+=M;r+=M;
		int ans=0;
		if (l>=r-1) return 0;
		for (;l^r^1;l>>=1,r>>=1)
		{
			if (~l&1) ans=max(ans,t[type][l+1]);
			if (r&1) ans=max(ans,t[type][r-1]);
		}
		return ans;
	}

}t;
int tt,n,ans;
int main()
{
	#ifndef NDEBUG
	freopen("poj3298.in","r",stdin);
	#endif
	scanf("%d",&tt);
	while (tt--)
	{
		ans=0;
		scanf("%d",&n);
		t=SegMentTree(n);
		for (int i=1;i<=n;i++)
		{
			int value=t.a[i];
			int value1=t.find(-INF,value,0)+1;
			int value2=t.find(value,INF,1)+1;
			t.insert(value,value1,1);
			ans=max(ans,value1);
			#ifndef NDEBUG
			cout<<"Add?> "<<value<<" f[i]="<<value1<<endl;
			#endif
			if (value2==1) continue;
			t.insert(value,value2,0);
			ans=max(ans,value2);
			#ifndef NDEBUG
			cout<<"Add?< "<<value<<" f[i]="<<value2<<endl;
			#endif
		}
		cout<<ans<<endl;
		#ifndef NDEBUG
		for (int i=1;i<=t.M*2;i++) if (t.t[0][i]) cout<<i<<':'<<t.t[0][i]<<' ';
		cout<<endl;
		#endif
	}

	return 0;
}

其实这题有更Easy的解法……贪心!

如果我们把原序列看成一条直线,且另a[0]和a[n+1]=-INF

那么如图所示


显然当最后的折现向下时:


显然解为凸点数*2

而当最后的折线向上时:



解为凸点数*2-1

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;
#define MAXN (30000+10)
int tt,n,a[MAXN];
int main()
{
	scanf("%d",&tt);
	while (tt--)
	{
		scanf("%d",&n);
		int ans=0;
		a[0]=a[n+1]=-0xfffffff;
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		for (int i=1;i<=n;i++) if (a[i-1]<a[i]&&a[i]>a[i+1]) ans++;
		ans*=2;
		if (a[n-1]<a[n]) ans--;
		cout<<ans<<endl;
	}

	return 0;
}



POJ 1631(O(nlogn)LIS的2种做法)

Language:
Bridging signals
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 8574   Accepted: 4635

Description

对于一个二分图的完全匹配,请找出最多的边使其两两不相交。

Input

第一行为测试数据数t,
对于每组数据,第一行为匹配数 p < 40000,
接下来p行,每行1个数a[i],表示左边第i个端点与右边第a[i]个端点相连

Output

对每组数据,输出一行ans,表示最大不相交匹配数

Sample Input

4
6
4
2
6
3
1
5
10
2
3
4
5
6
7
8
9
10
1
8
8
7
6
5
4
3
2
1
9
5
8
9
2
3
1
7
4
6

Sample Output

3
9
1
4

Source

这题显然可以转化为a[i]的LIS

LIS的一般做法如下:

f[i]表示以i为最后一个元素的最长序列数,

f[i]=f[j]+1(a[j]<a[i],j<i)

nLogn 算法1:

显然上面的方程有1维n是用来求‘小于a[i]且在a[i]前面的,最大的数‘

单从这个定义考虑,

于是问题转化成-维护序列max(f[i]),每一次增加1个点的值,求[1,value_i)的最大值(若无值则为0)

不妨用一个zkw线段树维护(本题max(f[i])的长度=n,若没有这个条件时间会退化到O(nLogT)(T为a[i]的最大值),那么请把原序列排序O(nLogn)+离散化O(n),这样复杂度就有O(nLogT)降至O(nLogn)    ).

程序1如下:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (40000+10)
#define NDEBUG
int t,n;
struct SegMentTree
{
	int a[MAXN*10],n;
	int M;
	SegMentTree(){}
	SegMentTree(int _n):n(_n)
	{
		M=1;
		while (M-2<n) M<<=1;
		memset(a,0,sizeof(a));
	}
	void insert(int _x,int c)
	{
		_x+=M;
		if (a[_x]<c)
		{
			a[_x]=c;
			for (_x>>=1;_x;_x>>=1) a[_x]=max(a[_x<<1],a[(_x<<1)^1]);
		}
	}
	int find(int l,int r)
	{
		int ans=0;
		l--;r++;
		l+=M;r+=M;
		while (l^r^1)
		{
			if (~l&1) ans=max(ans,a[l+1]);
			if (r&1) ans=max(ans,a[r-1]);
			l>>=1;r>>=1;
		}
		return ans;
	}
}a;
int main()
{
	#ifndef NDEBUG
	freopen("poj1631.in","r",stdin);
	#endif
	scanf("%d",&t);
	while (t--)
	{
		cin>>n;
		a=SegMentTree(n);
		for (int i=1;i<=n;i++)
		{
			int value;
			scanf("%d",&value);
			a.insert(value,a.find(1,value-1)+1);
		}
		printf("%dn",a.find(1,n));

	}
	return 0;
}

算法2:

仔细观察推导序列求最大值部分,发现i总从1开始[1,value_i)

于是可二分查找序列Max[I]'=Max[ F[p] ] (1≤p≤i)

Program LCS;
var
   a,d,f:array[1..100000] of longint;
   n,i,j,len,test:longint;
function search(k:longint):longint;
var
   i,j,m:longint;
begin
   i:=1; j:=len;
   m:=d[(i+j) div 2];
   while (i<=j) do
   begin
      m:=(i+j) div 2;
      if (d[m]<k) and (d[m+1]>=k) then exit(m)
      else if (d[m]<k) then i:=m+1
      else j:=m-1;
   end;
end;
begin
   read(test);
   while (test>0) do
   begin
      read(n);
      len:=1;
      fillchar(d,sizeof(d),0);
      for i:=1 to n do read(a[i]);
      d[1]:=a[1];
      f[1]:=1;
      for i:=2 to n do
      begin
         if (a[i]>d[len]) then
         begin
            inc(len);
            d[len]:=a[i];
            f[i]:=len;
         end
         else if (a[i]<=d[1]) then
         begin
            d[1]:=a[i];
            f[i]:=1;
         end
         else
         begin
            j:=search(a[i]);
            d[j+1]:=a[i];
            f[i]:=j+1;
         end;
      end;
      writeln(len);
      dec(test);
   end;
end.




CF 242E(zkw线段树-拆位)

E. XOR on Segment
time limit per test

4 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

对于数组,a1, a2, ..., an.
请维护2个操作。

  1. 区间[l, r], 求和.
  2. 将区间 [l, r],上的数异或x
Input

第一行为数组大小 n (1 ≤ n ≤ 105), 第二行为数组 a1, a2, ..., an(0 ≤ ai ≤ 106

第三行为操作数 m (1 ≤ m ≤ 5·104),接下来每行第1个数ti (1 ≤ ti ≤ 2)
表示进行第i种操作. ‘1
li, ri
 ‘(1 ≤ li ≤ ri ≤ n)表示[l,r]求和.‘2 li, ri, xi (1 ≤ li ≤ ri ≤ n, 1 ≤ xi ≤ 106)表示将区间 [l, r],上的数异或x

Output

输出所有操作1的结果。

不要用%lld 占位符,改用输入输出流或%I64占位符.

Sample test(s)
input
5
4 10 3 13 7
8
1 2 4
2 1 3 3
1 2 4
1 3 3
2 2 5 5
1 1 5
2 1 2 10
1 2 3
output
26
22
0
34
11
input
6
4 7 4 0 7 3
5
2 2 3 8
1 1 5
2 3 5 1
2 4 5 6
1 2 3
output
38
28

这题是xor拆位法的运用

显然我们可以用t[i,j]表示第i位上的1的个数,之后求和统计(这是xor操作优化的常用手段)

zkw线段树加Lazy标记时,用tot[]表示区间上的总数(其实也可以时事算出来)


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
using namespace std;
#define MAXN (100000+10)
#define MAXAi (1000000+10)
#define LogMAXAi (20)
#define MAXm (50000)
#define NDEBUG
int n,m,M,t[LogMAXAi+10][MAXN*10],tot[MAXN*10]={0};
bool b[LogMAXAi+10][MAXN*10]={0};
void build_tree()
{
	for (int i=M+1;i<=M+n;i++) tot[i]=1;
	for (int i=M-1;i>=1;i--) tot[i]=tot[i<<1]+tot[(i<<1)^1];

}
long long count_a_tree_node(int x)
{
	long long ans=0;
	for (int i=0,p=1;i<20;i++,p<<=1) ans+=(long long)(p)*(long long)(t[i][x]);
	return ans;
}
long long quere(int l,int r)
{
	long long ans=0;
	l--;r++;
	l+=M;r+=M;
	while (l^r^1)
	{
		if (~l&1) ans+=count_a_tree_node(l+1);
		if (r&1) ans+=count_a_tree_node(r-1);
		l>>=1;r>>=1;
	}
	return ans;
}
void pushdown(int x)
{
	if (x!=1) pushdown(x>>1);
	for (int i=0;i<20;i++)
	{
		if (b[i][x])
		{
			t[i][x<<1]=tot[x<<1]-t[i][x<<1];
			t[i][(x<<1)^1]=tot[(x<<1)^1]-t[i][(x<<1)^1];
			b[i][x]=0;b[i][x<<1]^=1;b[i][(x<<1)^1]^=1;
		}
	}
}
void update(int x)
{
	for (int i=0;i<20;i++)
	{
		int xx=x;
		for (x>>=1;x;x>>=1)
			t[i][x]=t[i][x<<1]+t[i][(x<<1)^1];
		x=xx;
	}
}
void a_t_xor(int j,int l,int r)
{
	l--;r++;
	l+=M;r+=M;
	while (l^r^1)
	{
		if (~l&1) {t[j][l+1]=tot[l+1]-t[j][l+1];b[j][l+1]^=1;	}
		if (r&1) {t[j][r-1]=tot[r-1]-t[j][r-1];b[j][r-1]^=1;	}
		l>>=1;r>>=1;
	}
}
void t_xor(int l,int r,int x)
{
	for (int i=0;x;i++,x>>=1) {if (x&1) a_t_xor(i,l,r);	}
}
int main()
{
//	freopen("CF_242E.in","r",stdin);
	memset(t,0,sizeof(t));
	scanf("%d",&n);
	M=1;while (M-2<n) M<<=1; //cout<<M<<endl;
	for (int i=M+1;i<=M+n;i++)
	{
		scanf("%d",&t[0][i]);
		int j=0;
		while (t[j][i]) {t[j+1][i]=t[j][i]/2;t[j][i]&=1;j++;}
	}
	for (int j=0;j<=19;j++)
	{
		for (int i=M-1;i>=1;i--) t[j][i]=t[j][i<<1]+t[j][(i<<1)^1];
	}
	#ifndef NDEBUG
	for (int i=1;i<=M+n;i++)
	{
		for (int j=0;j<=19;j++) cout<<t[j][i]<<' ';
		cout<<endl;
	}
	#endif
	build_tree();
	/*
	for (int i=1;i<=M+n;i++) cout<<tot[i]<<' ';
	cout<<endl;
	*/
	scanf("%d",&m);
	while (m)
	{
		int p;
		scanf("%d",&p);
		if (p==1)
		{
			int l,r;scanf("%d%d",&l,&r);
			pushdown(l-1+M);pushdown(r+1+M);
			cout<<quere(l,r)<<endl;
		}
		else
		{
			int l,r,x;
			scanf("%d%d%d",&l,&r,&x);
			pushdown(l-1+M);pushdown(r+1+M);
			t_xor(l,r,x);
			update(l-1+M);update(r+1+M);
		}

		#ifndef	NDEBUG
		for (int i=1;i<=M+n;i++)
		{
			for (int j=0;j<=19;j++) cout<<t[j][i]<<' ';
			cout<<endl;
		}
		#endif
		m--;
	}
	return 0;
}

RQNOJ 60(zkw线段树-xor区间与区间求和)

查看题目 Show Problem

题目:美丽的中国结

问题编号:60


题目描述

题目背景
kitty刚刚高三毕业.看到同学们都回家的回家,旅游的旅游,她的心里有些落寞.英俊潇洒风流倜傥迷倒万千KL却仅对kitty感冒的fish看在眼里,急在心里.一天,fish提出和kitty两个人一起外出旅游.kitty犹豫了几天,想好能瞒过家长的理由后(要问是什么……自己猜去),答应了.fish很高兴地带着kitty去登记了(别想歪,登记旅游团而已……),日照青岛五日游.
当然啦,他们玩得很高兴.虽然这次旅行是fish先提议的,但kitty因为玩得很畅快(刚高考完嘛),所以想送给fish一份礼物,一份能让见多识广的fish都无法忘怀的礼物.她从路边 9¾站台的某算命先生那里得知中国结具有增加RP的效果,而这正是fish所需要的,因此她决定动手给fish编一个奇特的中国结.

题目描述
中国结形式多样,fish会喜欢什么样的呢?思考几天后,kitty决定给fish编一个树状的中国结.这个中国结有n个结点(编号1,2,…,n),这n个结点之间总共恰有n-1条线相连,其中结点1上是树的根.这是一个奇特的中国结,因此它的编织方式也很奇特.在编织过程中的每一步骤,kitty有时需要将一个结点子树里的所有结点i的状态全部取反(如果原来结点i已经打结,则解开i,否则将结点i打结),有时又需要知道一个结点的子树里有多少已经打结的结点,你能帮助可爱的kitty完成这份礼物吗?

数据规模
对于40% 的数据,1≤n≤10000, 1≤m≤20000
对于100%的数据,1≤n≤100000, 1≤m≤100000

输入格式

输入
第一行有个整数n,表示这个中国结有n个结点
以下n-1行,每行有两个整数u和v(1≤u,v≤n),表示结点u和v间有一条线相连;
再一行有个整数m,表示kitty要进行的步骤数
以下m行,每行可能为:
"C x":表示将结点x的子树中所有结点的状态取反(包括x)

"Q x":表示kitty想知道结点x的子树中有多少已经打结的结点(包括x)

输出格式

对于每个“Q x”输出一行整数,表示结点x的子树中有多少已经打结的结点(包括x)

样例输入

5
1 2
1 3
2 4
2 5
5
Q 1
C 1
Q 1
C 2
Q 1

样例输出

0
5
2

zkw线段树在某个区间上取反(xor 1) ,求某个区间的和

zkw线段树的标记永久化在该题中不适用、

所以可以记一般的Lazy标记--

在处理区间时,把这个区间可能用到的点的标记全部下传

处理完区间后update左、右结点,把区间上传(这样就能消除标记上方结点为0对结果的影响)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (100000+10)
#define MAXm (100000+10)
#define NDEBUG
int n,m,M,t[MAXN*10]={0},l[MAXN],r[MAXN];
bool mark[MAXN*10]={false};
char s[50];
int head[MAXN*2]={0},next[MAXN*2],edge[MAXN*2],size=0;
void addedge(int u,int v)
{
	edge[++size]=v;
	next[size]=head[u];
	head[u]=size;
}
int tim=0;
bool b[MAXN]={0};
void dfs(int u)
{
	b[u]=1;l[u]=++tim;
	int p=head[u];
	while (p)
	{
		if (!b[edge[p]]) dfs(edge[p]);
		p=next[p];
	}
	r[u]=++tim;
}
void pushdown(int x,int p)
{
	if (!x) return;
	pushdown(x>>1,p<<1);
	if (mark[x>>1])
	{
		mark[x]^=1;mark[x^1]^=1;mark[x>>1]=0;
		t[x]=p-t[x];t[x^1]=p-t[x^1];
	}
	x>>=1;

}
void update(int x)
{
	for (x>>=1;x;x>>=1) t[x]=t[x<<1]+t[(x<<1)^1];
}
int main()
{
	#ifndef NDEBUG
	freopen("RQNOJ60.in","r",stdin);
	#endif
	scanf("%d",&n);
	for (int i=1;i<n;i++) {int u,v;scanf("%d%d",&u,&v);addedge(u,v);addedge(v,u);	}
	dfs(1);
	M=1;while (M-2<r[1]+1) M<<=1;
	scanf("%d",&m);
	#ifndef NDEBUG
//	cout<<M;
	for (int i=1;i<=n;i++) cout<<i<<':'<<l[i]<<'-'<<r[i]<<' ';
	cout<<'n';
	#endif
	for (int k=1;k<=m;k++)
	{
		int x;
		scanf("%s%d",s,&x);
		if (s[0]=='Q')
		{
			int ans=0,p1=l[x],p2=r[x];
			p1--;p2++;p1+=M;p2+=M;
			pushdown(p1,1);pushdown(p2,1);
//			cout<<"add: ";
			#ifndef NDEBUG
			cout<<p1<<' '<<p2<<endl;
			#endif
//			cout<<mark[10];
			while (p1^p2^1)
			{
				if (~p1&1) {/*pushdown(p1+1,1);*/ans+=t[p1+1];/*cout<<p1+1<<':'<<t[p1+1]<<' ';*/}
				if (p2&1)  {/*pushdown(p2-1,1);*/ans+=t[p2-1];/*cout<<p2-1<<':'<<t[p2-1]<<' ';*/}
				p1>>=1;p2>>=1;
			}
			cout<<ans/2<<endl;
		}
		else
		{
			int p=1,p1=l[x],p2=r[x];
			p1--;p2++;p1+=M;p2+=M;
			pushdown(p1,1);pushdown(p2,1);
			while (p1^p2^1)
			{
				if (~p1&1) {mark[p1+1]^=1;t[p1+1]=p-t[p1+1];/*cout<<p1+1<<' ';*/}
				if (p2&1) {mark[p2-1]^=1;t[p2-1]=p-t[p2-1];/*cout<<p2-1<<' ';*/}
				p1>>=1;p2>>=1;p<<=1;
			}
/*			for (int i=1;i<=M*2;i++) if (mark[i]) cout<<i<<':'<<t[i]<<' ';
			cout<<endl;*/
//			update(p1);

			update(l[x]-1+M);
/*			for (int i=1;i<=M*2;i++) if (mark[i]) cout<<i<<':'<<t[i]<<' ';
			cout<<endl;
*/			update(r[x]+1+M);

		}
		#ifndef NDEBUG
		for (int i=1;i<=n;i++) if (mark[i]) cout<<i<<' ';
		cout<<endl;
		#endif

	}



	return 0;
} 

HDU 4302(zkw线段树-单点修改区间最值)

Holedox Eating

Problem Description
Holedox在一条长度为L的线上. Holedox能在线上移动,线上会时不时的出现蛋糕(保证是整点)。Holedox会在想吃蛋糕时去最近的有蛋糕的点吃,如果左右的最近蛋糕与它的距离相等,它会按上一次行走的方向,如果没有蛋糕,它会呆在原地。
 


Input
第一行为数据组数T (1 <= T <= 10)
对每组数据,第一行有2个整数L,n(1<=L,n<=100000)表示线段长度和事件数。
接下来n行,每行一个事件:‘0 x'表示在x的位置出现一块蛋糕,’1‘表示Holedox去吃1块蛋糕
Holedox一开始在0点。
 


Output
对每组数据,输出一行‘Case i: dis',dis表示Holedox的移动距离。
 


Sample Input
3 10 8 0 1 0 5 1 0 2 0 0 1 1 1 10 7 0 1 0 5 1 0 2 0 0 1 1 10 8 0 1 0 1 0 5 1 0 2 0 0 1 1
 


Sample Output
Case 1: 9 Case 2: 4 Case 3: 2
 

这题是单点修改+区间最值的zkw线段树(a[]表示数量)

这题有几个技巧:

1.线段树多开;

2.'0'点的包含(把所有数字+1,显然不影响Holedox的移动距离)

3.额外信息的记录(为了把这题转换成线段树)


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
using namespace std;
#define MAXT (10+10)
#define MAXN (100000+10)
#define MAXm (100000+10)
#define INF (2139062143)
int n,m,M,T,now,direction,ans;
int t[2][MAXN*10];  //0->max 1->min
int a[MAXN];
void dec(int x)
{
	a[x]--;
	if (a[x]==0)
	{
		x+=M;
		t[0][x]=0;t[1][x]=INF;
		for (x>>=1;x;x>>=1)
		{
			t[0][x]=max(t[0][x<<1],t[0][(x<<1)^1]);
			t[1][x]=min(t[1][x<<1],t[1][(x<<1)^1]);
		}
	}
}
void go_left(int Lans)
{
	ans+=now-Lans;
	now=Lans;direction=-1;
	dec(now);
}
void go_right(int Rans)
{
	ans+=Rans-now;
	now=Rans;direction=1;
	dec(now);
}
int main()
{
	freopen("Holding.in","r",stdin);
	scanf("%d",&T);
	for (int k=1;k<=T;k++)
	{
		memset(t[0],0,sizeof(t[0]));ans=0;
		memset(t[1],127,sizeof(t[1]));
		memset(a,0,sizeof(a));now=1;direction=1; //1->right -1->left
		scanf("%d%d",&n,&m);n+=2;
		M=1;while (M-2<n) M<<=1;
		for (int i=1;i<=m;i++)
		{
			int p1,p2;
			scanf("%d",&p1);
			if (p1==0)
			{
				scanf("%d",&p2);p2+=1;
				a[p2]++;
				if (a[p2]==1)
				{
					p2+=M;t[0][p2]=t[1][p2]=p2-M;
					int p=p2;
					for (p>>=1;p;p>>=1) t[0][p]=max(t[0][p<<1],t[0][(p<<1)^1]);
					for (p2>>=1;p2;p2>>=1) t[1][p2]=min(t[1][p2<<1],t[1][(p2<<1)^1]);
				}
			}
			else
			{
				//(0,now+1)->[1,now]
				int l=M,r=now+1+M,Lans=0,Rans=INF;
				while (l^r^1)
				{
					if (~l&1) Lans=max(Lans,t[0][l+1]);
					if (r&1) Lans=max(Lans,t[0][r-1]);
					l>>=1;r>>=1;
				}
				//(now-1,n)->[now,n-1]
				l=now-1+M;r=n+M;
				while (l^r^1)
				{
					if (~l&1) Rans=min(Rans,t[1][l+1]);
					if (r&1) Rans=min(Rans,t[1][r-1]);
					l>>=1;r>>=1;
				}
				if (Lans==0&&Rans==INF) continue;
				else if (Lans==0) go_right(Rans);
				else if (Rans==INF) go_left(Lans);
				else if (now-Lans<Rans-now) go_left(Lans);
				else if (now-Lans>Rans-now) go_right(Rans);
				else if (now-Lans==0) dec(now);
				else if (direction==1) go_right(Rans);
				else go_left(Lans);




			}
			/*
				for (int j=1;j<=M*2;j++) if (t[0][j]) cout<<j<<":"<<t[0][j]<<' ';
				cout<<endl;
				for (int j=1;j<=M*2;j++) if (t[1][j]<INF) cout<<j<<":"<<t[1][j]<<' ';
				cout<<endl;
			*/




		}
		printf("Case %d: %dn",k,ans);

	}
	return 0;
}

POJ 2155(二维zkw线段树)

Language:
Matrix
Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 13137   Accepted: 4948

Description

给定一个矩阵(初始为0),维护2个操作:
1. C x1 y1 x2 y2 (1 <= x1 <= x2 <= n, 1 <= y1 <= y2 <= n) ,以(x1,y1)为左上角,(x2,y2)为右上角的矩阵取反。
2. Q x y (1 <= x, y <= n) 输出(x,y)的状态

Input

第一行为数据组数X (X <= 10)
每组数据第一行为N,T (2 <= N <= 1000, 1 <= T <= 50000) 表示矩阵边长与操作次数。
接下来T行,为Q或C操作 

Output

请输出所有提问结果。
每组数据后一回车。 

Sample Input

1
2 10
C 2 1 2 2
Q 2 2
C 2 1 2 1
Q 1 1
C 1 1 2 1
C 1 2 1 2
C 1 1 2 2
Q 1 1
C 1 1 2 1
Q 2 1

Sample Output

1
0
0
1

Source

POJ Monthly,Lou Tiancheng

这题是二维的线段树,

先建一棵线段树,再将它的所有结点(包括根)建立一个二叉树(维护这个结点)

异或仅需记录标记某点所对应的区间取反

查找时统计共记了几个标记即可(标记永久化)


Program Matrix;
const
   maxtt=10;
   maxn=1000;
   maxMM=50000;
var
   tt,n,mm,i,j,k,x1,y1,x2,y2,x,y,p1,p2,ans:longint;
   c:char;
   t:array[1..5000,1..5000] of boolean;
   M:longint;

Procedure change_y(x,y1,y2:longint);
var
   i,j:longint;
begin
   dec(y1);inc(y2);
   inc(y1,M);inc(y2,M);
   while ((y1 xor y2 xor 1)>0) do
   begin
      if ((y1 and 1)=0) then t[x,y1+1]:=not(t[x,y1+1]);
      if ((y2 and 1)=1) then t[x,y2-1]:=not(t[x,y2-1]);
      y1:=y1 shr 1;y2:=y2 shr 1;
   end;

end;

Procedure change_x(x1,y1,x2,y2:longint);
var
   i,j:longint;
begin
   dec(x1);inc(x2);
   inc(x1,M);inc(x2,M);
   while ((x1 xor x2 xor 1)>0) do
   begin
      if ((x1 and 1)=0) then change_y(x1+1,y1,y2);
      if ((x2 and 1)=1) then change_y(x2-1,y1,y2);
      x1:=x1 shr 1;x2:=x2 shr 1;
   end;
end;

function find_y(x,y:longint):boolean;
var
   i,j:longint;
begin
   inc(y,M); find_y:=false;
   while (y>0) do
   begin
      if (t[x,y]) then find_y:=not(find_y);
      y:=y shr 1;
   end;

end;
function find_x(x,y:longint):boolean;
var
   i,j:longint;
begin
   inc(x,M); find_x:=false;
   while (x>0) do
   begin
      find_x:=find_x xor find_y(x,y);
      x:=x shr 1;
   end;
end;
begin
   readln(tt);
   while (tt>0) do
   begin
      fillchar(t,sizeof(t),false);
      readln(n,mm);
      M:=1;
      while (M-2<n) do M:=M shl 1;
      for k:=1 to mm do
      begin
         read(c);
         if c='C' then
         begin
            readln(x1,y1,x2,y2);
            change_x(x1,y1,x2,y2);





         end
         else
         begin     //Q
            readln(x1,y1);
            if (find_x(x1,y1)) then writeln('1') else writeln('0');


         end;



      end;
      dec(tt);
      if (tt>0) then writeln;
   end;
end.

HDU 1754(zkw线段树-区间最值)

I Hate It


Problem Description
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。
这让很多学生很反感。

不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。

 


Input
本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。
接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。
当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。
 


Output
对于每一次询问操作,在一行里面输出最高成绩。
 


Sample Input
5 6 1 2 3 4 5 Q 1 5 U 3 6 Q 3 4 Q 4 5 U 2 9 Q 1 5
 


Sample Output
5 6 5 9
Hint
Huge input,the C function scanf() will work better than cin
 

zkw线段树中,解决区间最值问题的方法——差分

让我们以从点到根节点的路径上数的和为这个点的区间对应最大值。

很显然除根节点外,其它值皆为负数,且它必有一个儿子结点的值为0(叶结点除外)。


则当我们修改一个点的值时,先计算它原来的值,看看是否增加。

维护它与父节点的关系(它的修改值应减去路径上的值为它的当前值,而非直接修改(在原数上的增减除外)

求区间最值方法:

首先从子节点递归,存下它左右子节点所能递归到的最大值.

PS:1.记得每次向上加上路径上的值,因为它若取邻结点,则它们向上从父节点开始的部分完全相等。

      2.一开始那个点不能考虑(赋初值为-INF)

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<cctype>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (200000+10)
#define MAXm (5000+10)
#define INF (0xfffffff)
int n,m,M,t[MAXN*5];
char c[100];
int Query(int p1,int p2)
{
	p1--;p2++;p1+=M;p2+=M;
	int LANS=-INF,RANS=-INF;
	while (p1^p2^1)
	{
		LANS+=t[p1];RANS+=t[p2];
		if (~p1&1) LANS=max(LANS,t[p1+1]);
		if (p2&1)  RANS=max(RANS,t[p2-1]);
		p1>>=1;p2>>=1;
	}
	LANS+=t[p1];RANS+=t[p2];
	int ANS=max(LANS,RANS);
	for (p1>>=1;p1;p1>>=1) ANS+=t[p1];
	return ANS;
}
int main()
{
//	freopen("I hate it.in","r",stdin);
	while (scanf("%d%d",&n,&m)!=EOF)
	{
		memset(t,0,sizeof(t));
		M=1;while (M-2<n) M<<=1;
		for (int i=M+1;i<=M+n;i++) scanf("%d",&t[i]);
		for (int i=M;i>=1;i--)
		{
			t[i]=max(t[i<<1],t[(i<<1)^1]);
			t[i<<1]-=t[i];t[(i<<1)^1]-=t[i];
		}

		for (int k=1;k<=m;k++)
		{
			int p1,p2;
			scanf("%s%d%d",c,&p1,&p2);
			if (c[0]=='Q')
			{
			/*	p1--;p2++;p1+=M;p2+=M;
				int LANS=-INF,RANS=-INF;
				while (p1^p2^1)
				{
					LANS+=t[p1];RANS+=t[p2];
					if (~p1&1) LANS=max(LANS,t[p1+1]);
					if (p2&1)  RANS=max(RANS,t[p2-1]);
					p1>>=1;p2>>=1;
				}
				LANS+=t[p1];RANS+=t[p2];
				int ANS=max(LANS,RANS);
				for (p1>>=1;p1;p1>>=1) ANS+=t[p1];
			*/	cout<<Query(p1,p2)<<endl;
			}
			else
			{
				p1+=M;
				t[p1]+=p2-Query(p1-M,p1-M);
				for (p1>>=1;p1;p1>>=1)
				{
					int A=max(t[p1<<1],t[(p1<<1)^1]);
					t[p1<<1]-=A;t[(p1<<1)^1]-=A;t[p1]+=A;
				}
			}

		}
	}
	return 0;
}