51nod 1376: 最长递增子序列的数量(二维偏序+cdq分治)

Posted Curtis

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51nod 1376: 最长递增子序列的数量(二维偏序+cdq分治)相关的知识,希望对你有一定的参考价值。

www.cnblogs.com/shaokele/


1376 最长递增子序列的数量##

  Time Limit: 1 Sec   Memory Limit: 128MB
  分值: 160        难度:6级算法题

Description###

  数组A包含N个整数(可能包含相同的值)。设S为A的子序列且S中的元素是递增的,则S为A的递增子序列。如果S的长度是所有递增子序列中最长的,则称S为A的最长递增子序列(LIS)。A的LIS可能有很多个。例如A为:{1 3 2 0 4},1 3 4,1 2 4均为A的LIS。给出数组A,求A的LIS有多少个。由于数量很大,输出Mod 1000000007的结果即可。相同的数字在不同的位置,算作不同的,例如 {1 1 2} 答案为2。

Input###

  第1行:1个数N,表示数组的长度。(1 <= N <= 50000)
  第2 - N + 1行:每行1个数A[i],表示数组的元素(0 <= A[i] <= 10^9)

Output###

  输出最长递增子序列的数量Mod 1000000007。

Sample Input###

  5
  1
  3
  2
  0
  4

Sample Output###

  2

题目地址: 51nod 1376: 最长递增子序列的数量####

题目大意: 求最长上升子序列的个数####

www.cnblogs.com/shaokele/


题解:####

  显然这道题目有很多做法 (知乎)
  然而这里只做cdq分治
  LIS其实是一个二维偏序问题(下标和值)
  分治的套路:
  第一维排序,第二维分治
  1. 先递归求解区间左半边
  2. 用左边的值更新右边,相当于处理跨越区间中点的情况
  3. 递归求解区间右半边
  

//by:知乎 李贝瑀

bool cmp(int i, int j) { return a[i] != a[j]? a[i] < a[j]: i > j; }

void cdq(int l, int r)
{
        if (l == r) {
                if (!f[l]) f[l] = 1;
                return;
        }
        int mid = (l + r) >> 1;
        cdq(l, mid);
        for (int i = l; i <= r; ++i) id[i] = i;
        sort(id + l, id + r + 1, cmp);
        int len = 0;
        for (int i = l; i <= r; ++i) {
                if (id[i] <= mid) len = max(len, f[id[i]]);
                else f[id[i]] = max(f[id[i]], len + 1);
        }
        // code
        cdq(mid + 1, r);
}

  上面这个写法其实是 \\(O(Nlog^2N)\\) 的,因为对id排序时调用了sort,可以改成归并排序

  对于求解方案数,只需要在注释的地方加上统计的代码
  过程和前面基本类似,能理解的话很容易写出来


AC代码

#include <cstdio> 
#include <algorithm>
#define mp make_pair
#define pr pair<int,int>
using namespace std;
const int N=50005,mo=1000000007;
int n;
int a[N],id[N];
pr f[N];
bool cmp(int x,int y){
	if(a[x]!=a[y])return a[x]<a[y];
	return x>y;
}
pr Max(pr a,pr b){
	if(a.first>b.first)return a;
	if(a.first<b.first)return b;
	if((a.second+=b.second)>=mo)
		a.second-=mo;
	return a;
}
void cdq(int l,int r){
	if(l==r)return;
	int mid=(l+r)/2;
	cdq(l,mid);
	for(int i=l;i<=r;i++)id[i]=i;
	sort(id+l,id+r+1,cmp);
	pr mx=mp(0,0);
	for(int i=l;i<=r;i++){
		int k=id[i];
		if(k<=mid)mx=Max(mx,f[k]);
		else{
			pr now=mx;
			now.first++;
			f[k]=Max(f[k],now);
		}
	}
	cdq(mid+1,r);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
		f[i]=mp(1,1);
	cdq(1,n);
	pr ans=mp(0,0);
	for(int i=1;i<=n;i++)
		ans=Max(ans,f[i]);
	printf("%d\\n",ans.second);
	return 0;
}

以上是关于51nod 1376: 最长递增子序列的数量(二维偏序+cdq分治)的主要内容,如果未能解决你的问题,请参考以下文章

最长递增子序列的数量 51Nod - 1376

51nod1376 最长递增子序列的数量

51NOD1376最长递增子序列的数量

51nod 1376 最长递增子序列的数量

51nod1376 最长递增子序列的数量

51nod 1376 最长递增子序列的数量(不是dp哦,线段树 +  思维)