HDU 3265(矩形面积并-分割矩形)

Posters

Problem Description
有很多海报,每个海报都被减去了一个矩形(可能全减),现在给出没张海报的位置,求它们的面积并。
保证坐标在(0, 0)到(50000, 50000)之间。没有海报斜着贴,减去的矩形边与x,y轴平行。
 
Input
有很多数据。
对于每组数据第一行有一个整数N (0<N<=50000)表示海报数 接下来n行,每行8个整数 x1, y1, x2, y2, x3, y3, x4, y4, 表示每张海报的左下角坐标(x1, y1),右上角坐标(x2, y2) ,减去的矩形左上角坐标(x3, y3),右上角坐标(x4, y4)(0<=xi, yi<=50000(i=1…4) and x1<=x3<x4<=x2, y1<=y3<y4<=y2).

数据以0结尾。
 


Output
对于每组数据输出一行,表示它们的面积并。
 


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


Sample Output
56
 

这题就是要把矩形差分成如下形式:



然后进行矩形面积并:

谨记问题转化后各数组的大小。


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (50000+10)
#define MAXT (MAXN*5)
#define MAXXi (50000+10)
#define Lson (x<<1)
#define Rson ((x<<1)^1)
int hpos[MAXN];
int x[MAXN*4]={0};
struct SegMent
{
    int x1,x2,h,type;
    SegMent(){}
    SegMent(int _x1,int _x2,int _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;}
};
int n,size;
struct SegMent_array
{
    SegMent a[MAXN*8];
    int size;
    SegMent_array():size(0)
    {
        memset(a,0,sizeof(a));
    }

    bool add(int x1,int x2,int y1,int y2)
    {
        if (x1==x2||y1==y2) return 0;
        else
        {
            a[++size]=SegMent(x1,x2,y1,1);
            a[++size]=SegMent(x1,x2,y2,-1);
            return 1;
        }
    }
}a;
struct SegMentTree
{
    long long sum[MAXT],cnt[MAXT],len[MAXT];
    int M,n;
    void fillchar(int _n)
    {
        n=_n;
        M=1;while (M-2<n) M<<=1;
        memset(sum,0,sizeof(sum));
        memset(cnt,0,sizeof(cnt));
        memset(len,0,sizeof(len));
        for (int i=M+1;i<=M+n;i++) len[i]=x[i-M+1]-x[i-M];
        for (int i=M-1;i>=1;i--) len[i]=len[i<<1]+len[(i<<1)^1];
    }
    void pushup(int x)
    {
		sum[x]=(cnt[x])?(len[x]):((x<M)?(sum[Lson]+sum[Rson]):0);
	}
	void update(int x)
	{
		while (x)
		{
			pushup(x);x>>=1;
		}
	}
    void insert(int l,int r,long long c)
	{
		if (l>r) return ;
		l=l-1+M;r=r+1+M;
		int ll=l,rr=r;
		for (;l^r^1;l>>=1,r>>=1)
		{
			if (~l&1) {cnt[l+1]+=c; pushup(l+1);}
			if (r&1) {cnt[r-1]+=c; pushup(r-1);}
		}
//		cout<<endl<<"Push:"<<ll<<' '<<rr<<endl;
		update(ll);update(rr);
	}
	void print()
	{
		for (int i=1;i<=2*M;i++) if (sum[i]) cout<<i<<':'<<sum[i]<<' ';
		cout<<endl;
		for (int i=1;i<=2*M;i++) if (cnt[i]) cout<<i<<':'<<cnt[i]<<' ';
		cout<<endl;
		cout<<endl;
	}
}t;


int main()
{
//  freopen("Hdu3265.in","r",stdin);
    while (scanf("%d",&n)!=EOF)
    {
		if (n==0) break;
	    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);
    	    a.add(x1,x3,y1,y4);
        	a.add(x3,x2,y1,y3);
	        a.add(x1,x4,y4,y2);
    	    a.add(x4,x2,y3,y2);
			x[i*4-3]=x1;x[i*4-2]=x2;x[i*4-1]=x3;x[i*4]=x4;
	    }
    	sort(x+1,x+4*n+1);
    	size=unique(x+1,x+4*n+1)-(x+1);
/* 		for (int i=1;i<=size;i++) cout<<x[i]<<' ';
    	cout<<endl;
*/
//		cout<<size;
    	for (int i=1;i<=size;i++) hpos[x[i]]=i;
		t.fillchar(size-1);

		sort(a.a+1,a.a+1+a.size);
//		cout<<'s';
//		for (int i=1;i<=a.size;i++) cout<<a.a[i].x1<<' '<<a.a[i].x2<<' '<<a.a[i].h<<' '<<a.a[i].type<<endl;
		long long ans=0;
		for (int i=1;i<=a.size;i++)
		{
			ans+=t.sum[1]*((long long)a.a[i].h-a.a[i-1].h);
			t.insert(hpos[a.a[i].x1],hpos[a.a[i].x2]-1,a.a[i].type);
/*			cout<<a.a[i].x1<<' '<<a.a[i].x2<<' '<<a.a[i].h<<' '<<a.a[i].type<<endl;
			cout<<ans<<endl;
			t.print();
*/		}
		cout<<ans<<endl;
		a.size=0;
	}
	return 0;
}




POJ 1151(矩形面积并)

Language:
Atlantis
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 13134   Accepted: 5050

Description

There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the
total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.

Input

The input consists of several test cases. Each test case starts with a line containing a single integer n (1 <= n <= 100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0 <= x1 < x2 <=
100000;0 <= y1 < y2 <= 100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area. 
The input file is terminated by a line containing a single 0. Don't process it.

Output

For each test case, your program should output one section. The first line of each section must be "Test case #k", where k is the number of the test case (starting with 1). The second one must be "Total explored area: a", where a is the total explored area
(i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point. 
Output a blank line after each test case.

Sample Input

2
10 10 20 20
15 15 25 25.5
0

Sample Output

Test case #1
Total explored area: 180.00 

Source

Hdu 1542,不过数据变水……


#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;
}

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 3487(稳定婚姻问题)

Language:
The Stable Marriage Problem
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 1941   Accepted: 827

Description

稳定婚姻系统问题如下

  • 集合M 表示n个男性;
  • 集合 F 表示n个女性;
  • 对于每个人我们都按异性的中意程度给出一份名单(从最中意的到最不中意的).

如果没有(mf) , f ∈ F,m ∈ M f对m比对她的配偶中意的同时mf比对他的配偶更加中意,那这个婚姻是‘稳定’的.如果一个稳定配对不存在另一个稳定婚姻配对,其中所有的男性的配偶都比现在强,那么这个稳定配对称为男性最优配对。

Input

第一行为数据数.

对于每组数据,第一行为n (0 < n < 27).接下来一行依次为男性的名字(小写字母)和女性的名字(大写字母)接下来 n 行为每个男性的中意程度名单(从大到小),接下来n行是女性的中意程度名单(同上).

Output

输出一组男性最优配对,男性按字典序从小到大排列,每行为“男性名 女姓名”的形式;

每数据后输出一空行。

Sample Input

2
3
a b c A B C
a:BAC
b:BAC
c:ACB
A:acb
B:bac
C:cab
3
a b c A B C
a:ABC
b:ABC
c:BCA
A:bac
B:acb
C:abc

Sample Output

a A
b B
c C

a B
b A
c C

Source

这题是很经典的稳定婚姻系统问题,它的解法为“求婚-拒绝”算法:

======================================================================================================

稳定婚姻问题的经典算法为求婚-拒绝算法(propose-and-reject algorithm),即
男士按自己喜欢程度从高到低依次给每位女士主动求婚,直到有一个接受他。女士每
次遇到比当前配偶更差的男士时拒绝他,遇到更喜欢的男士时就接受他,并抛弃以前
的配偶。被抛弃的男士继续按照列表向剩下的女士依次求婚,直到所有人都有配偶。
看起来女士更有选择权,但实际上最后得到的结果是男士最优(man-optimal)的。下
面我们详细描述这一算法并证明相应的结论。
如果算法最后得到了一个匹配,那么它一定是稳定的。为了证明这一点,我们首
先注意到随着算法的执行,每位女士的配偶越来越好,而每位男士的配偶越来越差。
因此假设男士u和女士v形成不稳定对,u一定曾经向v求过婚,但被拒绝。这说明v当时
的配偶比u更好,因此算法结束后的配偶一定仍比u好,和不稳定对的定义矛盾。
下面我们只需要说明算法一定成功结束,即不会存在一位男士u,使得他向所有
女士求婚后仍为单身。假设存在这样的人,设其中最后一次被抛弃时刻最晚的男士
为u,则他最后一次被抛弃时无配偶,因此当时一定存在女士v也没有配偶。由于v是单
身的,所以一定没有人向她求过婚,因此v还在u的考虑范围之中,以后会向v求婚。到
432 图论问题和算法
时u会再次有配偶,要么还将被抛弃一次,要么最后不会单身,无论哪种情况都将产生
矛盾。
这样,我们证明了算法一定得到稳定匹配。时间复杂度显然是O(n2),因此每个男
士最多考虑每个女士各一次,每次的时间复杂度均为O(1)。显然这已经是时间复杂度
的下限了,因为输入是O(n2)的。
如果存在一个稳定匹配使得男士i和女士j配对,则称(i,j)是稳定对。对于每个男
士i,设所有稳定对(i,j)中i 最喜欢的女士为best(i),则可以证明这里给出的算法对让每
位男士i与best(i)配对。对于所有男士来说,不会有比这更好的结果了,而对于女士则
恰恰相反——对于她们来说不会有比这更糟的结果了。

=================================================================================================================


Program p3487;
const
   maxn=26;
var
   tt,n,i,j,k:longint;
   nam:array[1..2,1..maxn] of char;
   female_like:array['A'..'Z','a'..'z'] of longint;
   male_like:array['a'..'z',1..maxn] of char;

   a:array['a'..'z'] of char;
   b:array['A'..'Z'] of char;

   s:string;
   c:char;
   q:array[1..maxn*maxn] of char;
   h,t:longint;
   now,v:char;
begin
   readln(tt);
   while (tt>0) do
   begin
      fillchar(a,sizeof(a),' ');
      fillchar(b,sizeof(b),' ');
      readln(n);
      for k:=1 to 2 do
         for i:=1 to n do
         begin
            read(nam[k,i]);
            read(c);
         end;
      readln;
      for i:=1 to n do
      begin
         readln(s);
         for j:=3 to length(s) do male_like[s[1],j-2]:=s[j];
      end;
      for i:=1 to n do
      begin
         readln(s);
         for j:=3 to length(s) do female_like[s[1],s[j]]:=n+1-(j-2);
      end;

      h:=1;t:=n;
      for i:=1 to n do q[i]:=nam[1,i];
      while h<=t do
      begin
         now:=q[h];
         for i:=1 to n do
         begin
            v:=male_like[now,i];
            if (b[v]=' ') then
            begin
               a[now]:=v;
               b[v]:=now;
               break;
            end;
            if (female_like[v,b[v]]<female_like[v,now]) then
            begin
               inc(t);
               q[t]:=b[v];
               a[b[v]]:=' ';
               a[now]:=v;
               b[v]:=now;
               break;
            end;
         end;
         inc(h);
      end;


      for now:='a' to 'z' do if a[now]<>' ' then writeln(now,' ',a[now]);
      writeln;


      dec(tt);
   end;

end.

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;
}

BZOJ 2547(匈牙利算法-任意边的处理)

2547: [Ctsc2002]玩具兵

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 104  Solved: 50
[Submit][Status][Discuss]

Description

小明的爸爸给他买了一盒玩具兵,其中有 K个步兵,K个骑兵和一个天兵,个个高大威猛,形象逼真。盒子里还有一个M*N棋盘,每个格子(i,j)都有一个高度Hij,并且大得足以容纳所有的玩具兵。小明把所有的玩具兵都放到棋盘上去,突然想到了一种很有趣的玩法:任意挑选T个不同的格子,并给每个格子i规定一个重要值Ri­­,游戏的目标就是每次沿东南西北之一的方向把一个玩具兵移动到其相邻的格子中(但不能移动到棋盘外面去),最终使得每个挑选出的格子i上恰好有Ri个玩具兵。小明希望所有的玩具兵都在某个选定的格子中,因此他总是使选出的T个格子的重要值之和等于玩具兵的个数。为了增加难度,小明给玩具兵们的移动方式做了一些规定:

  步兵只会往高处爬,因此如果两个格子AB相邻,当且仅当格子A的高度小于或等于B,步兵才可以从A移动到B

      骑兵只会往低处跳,因此如果两个格子AB相邻,当且仅当格子A的高度大于或等于B,骑兵才可以从A移动到B

      天兵技术全面,移动不受任何限制。

       可是没玩几次,小明就发现这个游戏太难了,他常常玩了好半天也达不到目的。于是,他设计了一种“超能力”,每使用一次超能力的时候,虽然不能移动任何一个玩具兵,但可对它们进行任意多次交换操作,每次交换两个玩具兵。等这次超能力使用完后又可和平常一样继续移动这些玩具兵。借助强大的超能力,这个游戏是容易玩通的,但是怎样才能让使用超能力的次数最少呢?

 

Input

第一行包含四个整数:M,N,K,T (2<=M,N<=100, 1<=K<=50, 1<=T<=2K+1)

    第二行包括2K+1个数对(xi,yi),代表各个玩具兵的初始位置。前K个代表兵,接下来的K个代表骑兵,最后一个代表兵。

    第三行包含T个三元组(xi,yi,ri),第i组代表第i个目标格的位置和重要值。

        以下M行,每行N个整数。其中第i行第j个数为即格子的高度Hij。高度是不超过100的正整数,注意:不同玩具兵的初始位置可能相同。输入数据保证无错,选定的T个格子的重要值之和保证等于2K+1

Output

仅包含一行,即使用超能力的最小次数T

Sample Input

4 6 2 5

1 1 1 5 4 1 4 5 3 3

1 2 1 2 6 1 3 2 1 3 6 1 4 3 1

3 2 6 1 3 5

2 1 7 4 4 6

2 3 1 4 3 4

4 3 4 3 2 3

Sample Output

1

先来分析一下这个问题。
1.显然天兵的初始位置不重要。
2.最坏情况不断使用天兵,2*k次便能解决.

不妨假设不存在天兵:
1.我们把调换位置-改为调换兵种(显然成立),那么一个棋子一定调换(否则移到需要的位置调换).
2.显然如果我有确定的‘超能力’次数,那么任何一个棋子所能到达的地方有限(bfs)。
3.显然我们可以判断1个棋子在超能力步数内必须到达另一个棋子(就是这个棋子,无论怎么调换兵种).
问是否是合法方案   
那么这就是一个二分图匹配,必须完全匹配。

天兵的意义
1.显然一个天兵可以在棋盘上随便走,在每次调换时,找一个棋子把它挪到目的地
   这相当于在二分图上任意填‘超能力次数’条边。
故先算出最大匹配数,再+超能力数,可以得到最优方案

模型建毕:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN (100+10)
#define MAXM (100+10)
#define MAXK (100+10)
#define MAXT (100+1+10)
#define COST (bool(type)^(f[x][y]&1))?(h[x][y]<h[x+path[i][0]][y+path[i][1]]):(h[x][y]>h[x+path[i][0]][y+path[i][1]])
const int path[4][2]={{0,1},{1,0},{-1,0},{0,-1}};
int n,m,k,t,h[MAXM][MAXN],f[MAXM][MAXN];
queue< pair<int,int> > q;
bool inside(int x,int y)
{
	if (1<=x&&x<=m&&1<=y&&y<=n) {/*cout<<'A'<<x<<' '<<y<<endl;*/ return 1;}else return 0;
}
void bfs(bool type,int x,int y)  //type-> 0 upper 1 lower
{
	memset(f,127,sizeof(f));
	f[x][y]=0;
	q.push(make_pair(x,y));
	while (!q.empty())
	{
		pair<int ,int> now=q.front();
		int &x=now.first,&y=now.second;
		for (int i=0;i<4;i++)
		{
			int c=COST;
			if (inside(x+path[i][0],y+path[i][1]))
				if (f[x+path[i][0]][y+path[i][1]]>f[x][y]+c)
			{
		//		cout<<(x+path[i][0])<<' '<<(y+path[i][1])<<endl;
				f[x+path[i][0]][y+path[i][1]]=f[x][y]+c;
				q.push(make_pair(x+path[i][0],y+path[i][1]));
			}
		}
		q.pop();
	}
}
pair<int,int> A[MAXT],B[MAXT];
int cost[MAXK][MAXT],a[MAXT];
bool b[MAXT];
bool find(int x,int maxcost)
{
	for (int i=1;i<=2*k+1;i++)
		if (!b[i]&&cost[x][i]<=maxcost)
		{
			b[i]=1;
			if (a[i]==0) {a[i]=x;return 1;	}
			if (find(a[i],maxcost)) {a[i]=x; return 1;	}
		}
	return 0;
}
int hopcroft(int maxcost)
{
	memset(a,0,sizeof(a));
	int ans=0;
	for (int i=1;i<=2*k;i++)
	{
		memset(b,0,sizeof(b));
		if (find(i,maxcost)) ans++;
	}
//	cout<<ans<<endl;
	return ans;
}
int b_search()
{
	int l=0,r=2*k;
	while (l<r)
	{
		m=(l+r)>>1;
		if (hopcroft(m)+m>=2*k) r=m;
		else l=m+1;
	}
	return l;
}
int main()
{
//	freopen("2547.in","r",stdin);
	scanf("%d%d%d%d",&m,&n,&k,&t);
	for (int i=1;i<=2*k+1;i++) scanf("%d%d",&A[i].first,&A[i].second);
	int size=1;
	for (int i=1;i<=t;i++)
	{
		int r;
		scanf("%d%d%d",&B[size].first,&B[size].second,&r);
		size++;
		for (r--;r;r--,size++) B[size]=B[size-1];
	}
	for (int i=1;i<=m;i++)
		for (int j=1;j<=n;j++)
			scanf("%d",&h[i][j]);

/*	bfs(1,1,2);
	for (int i=1;i<=m;i++)
	{
		for (int j=1;j<=n;j++) cout<<f[i][j]<<' ';
		cout<<'n';
	}
*/
	for (int i=1;i<=2*k;i++)
	{
		bfs((i>k),A[i].first,A[i].second);
		for (int j=1;j<=2*k+1;j++)
		{
			cost[i][j]=f[B[j].first][B[j].second];
//			cout<<i<<' '<<j<<' '<<cost[i][j]<<endl;
		}
	}

	cout<<b_search()<<endl;


	return 0;
}

C++ pair

Pair类型概述

pair是一种模板类型,其中包含两个数据值,两个数据的类型可以不同,基本的定义如下:

 

pair<int, string> a;

表示a中有两个类型,第一个元素是int型的,第二个元素是string类型的,如果创建pair的时候没有对其进行初始化,则调用默认构造函数对其初始化。

 

pair<string, string> a("James", "Joy");

也可以像上面一样在定义的时候直接对其初始化。

 

由于pair类型的使用比较繁琐,因为如果要定义多个形同的pair类型的时候,可以时候typedef简化声明:

typedef pair<string, string> author;

author pro("May", "Lily");

author joye("James", "Joyce");

 

 

Pair对象的操作

 

  • 于pair类,由于它只有两个元素,分别名为first和second,因此直接使用普通的点操作符即可访问其成员

pair<string,
string> a("Lily", "Poly"); 

string
name;

name
= pair.second;

  • 生成新的pair对象

可以使用make_pair对已存在的两个数据构造一个新的pair类型:

int a = 8;

string m = "James";

pair<int, string> newone;

newone = make_pair(a, m);

 

 

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.