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.