NKOJ 3500 独立集(dp)

来源:互联网 发布:javascript focus用法 编辑:程序博客网 时间:2024/05/20 22:03

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为结尾的最长上升子序列长度为k,那么将A[i]加到一个setP中,将i加到另一个set,Q中,那么跑完最长上升子序列后,令第一问答案为len

len开始倒着讨论,用P[k],Q[k]中最大的元素去删除P[k1],Q[k1]中的元素,删除条件就是如果P[k1],Q[k1]中的一个元素不能转移到P[k],Q[k]中的任一元素,那么删除。

如果讨论时发现某set中仅有一个元素,那么这个元素一定要选。复杂度nlogn

优秀做法参见THH


代码:

#include<stdio.h>#include<iostream>#include<algorithm>#include<cstring>#include<set>#define N 100055using 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]);}
原创粉丝点击