SSL2756 2017年10月5提高组T1 独立集(最长上升子序列+二分)

来源:互联网 发布:德州力拓软件 编辑:程序博客网 时间:2024/06/09 23:10

2017年10月5提高组T1 独立集

Description

有一天,一个名叫顺旺基的程序员从石头里诞生了。又有一天,他学会了冒泡排序和独立集。在一个图里,独立集就是一个点集,满足任意两个点之间没有边。于是他就想把这两个东西结合在一起。众所周知,独立集是需要一个图的。那么顺旺基同学创造了一个算法,从冒泡排序中产生一个无向图。

这个算法不标准的伪代码如下:

procedure bubblesortgraph(n, a[]) :

                 /*输入:点数n,1到n的全排列a。                    输出:一个点数为n的无向图G。*/               创建一个有n个点,0条边的无向图G。                repeat                     swapped = false                     for i 从 1 到 n-1 :                           if a[i] > a[i + 1] :                                 在G中连接点a[i]和点a[i + 1]                                 交换a[i]和a[i + 1]                                 swapped = true                until not swapped                输出图G。                 //结束。 

那么我们要算出这个无向图G最大独立集的大小。但是事情不止于此。顺旺基同学有时候心情会不爽,这个时候他就会要求你再回答多一个问题:最大独立集可能不是唯一的,但有些点是一定要选的,问哪些点一定会在最大独立集里。今天恰好他不爽,被他问到的同学就求助于你了。

Input

两行。第一行为N,第二行为1到N的一个全排列。

Output

两行。第一行输出最大独立集的大小,第二行从小到大输出一定在最大独立集的点的编号(输入时的序号)。

分析:观察给出的伪蟒蛇代码可以发现,当且仅当两个数为逆序对的时候有边。那么对于独立集,两两之间互不为逆序对。同样观察发现这就是上升子序列。那么最大独立集就是最长上升子序列了。至于必定会在最长上升子序列里的元素,首先要满足他能在最长上升子序列里,其次是要他到左边的最长下降子序列的长度和到右边的最长上升子序列长度独一无二(否则就可以交换)。这样做两遍最长子序列就可以了。

代码

#include <cstdio>#define maxn 200000using namespace std;int a[maxn],f[maxn],l[maxn],r[maxn];int ans,n;int v[maxn];int find(int l,int r,int x){    while (l<=r)    {        int mid=(l+r)/2;        if (x>f[mid]) l=mid+1;            else r=mid-1;    }    return l;}int main(){    scanf("%d",&n);    for (int i =1;i<=n;i++)        scanf("%d",&a[i]);    ans=0;    for (int i=1;i<=n;i++)    {        int j=find(1,ans,a[i]);        f[j]=a[i];        if (j>ans) ans=j;        l[i]=j;    }    int an=0;    for (int i=n;i>=1;i--)    {        int j=find(1,an,-a[i]);        f[j]=-a[i];        if (j>an) an=j;        r[i]=j;    }    printf("%d\n",ans);    for (int i=1;i<=n;i++)        if (l[i]+r[i]==ans+1) v[l[i]]++;    for (int i=1;i<=n;i++)        if (v[l[i]]==1&&l[i]+r[i]==ans+1) printf("%d ",i);}