P3500【2015多校联训6】独立集
问题描述
输入格式
输入包含两行,第一行为 N,
第二行为 1 到 N 的一个全排列
输出格式
输出包含两行,第一行输出最大独立集的大小,第二行从小到大输出一定在最大独立集 的点的编号。
样例输入
3
3 1 2
样例输出
2
2 3
提示
30%的数据满足 N<=16
60%的数据满足 N<=1,000
100%的数据满足 N<=100,000
仔细观察,发现第一问就是最长上升子序列,因为逆序对才会连边,那么没有边相连的点一定是正序的,那么问题就是求最长正序,即最长上升子序列。
考虑第二问,优秀的做法做不来,搞个暴力。
假设以
i
为结尾的最长上升子序列长度为
从 len 开始倒着讨论,用 P[k],Q[k] 中最大的元素去删除 P[k−1],Q[k−1] 中的元素,删除条件就是如果 P[k−1],Q[k−1] 中的一个元素不能转移到 P[k],Q[k] 中的任一元素,那么删除。
如果讨论时发现某 set 中仅有一个元素,那么这个元素一定要选。复杂度 nlogn
优秀做法参见THH
代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
#define N 100055
using namespace std;
set<int>Q[N],R[N];
int n,A[N],F[N],B[N],id[N],ans;
bool mark[N];
int main()
{
set<int>::iterator x,y,z;
int i,j,k;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&A[i]),id[A[i]]=i;
memset(B,60,sizeof(B));B[0]=0;Q[0].insert(0);
for(i=1;i<=n;i++)
{
F[i]=lower_bound(B+1,B+n+1,A[i])-B;
B[F[i]]=A[i];
Q[F[i]].insert(A[i]);
R[F[i]].insert(i);
ans=max(ans,F[i]);
}
for(i=ans;i>=1;i--)
{
if(Q[i].size()==1)
{
x=Q[i].begin();
mark[*x]=1;
}
x=Q[i].end();x--;
y=Q[i-1].lower_bound(*x);
for(z=y;z!=Q[i-1].end();z++)R[i-1].erase(R[i-1].find(id[*z]));
Q[i-1].erase(y,Q[i-1].end());
x=R[i].end();x--;
y=R[i-1].lower_bound(*x);
for(z=y;z!=R[i-1].end();z++)Q[i-1].erase(Q[i-1].find(A[*z]));
R[i-1].erase(y,R[i-1].end());
}
printf("%d\n",ans);
for(i=1;i<=n;i++)if(mark[i])printf("%d ",id[i]);
}