「重庆市NOIP模拟赛」独立集

前言

知道做法竟然可以 \(\tt \color{red}{WA}\) 3发,真是没谁了。

题面远长于题解系列。

题目

有一天,一个名叫顺旺基的程序员从石头里诞生了。又有一天,他学会了冒泡排序和独立集。在一个图里,独立集就是一个点集,满足任意两个点之间没有边。于是他就想把这两个东西结合在一起。

众所周知,独立集是需要一个图的。那么顺旺基同学创造了一个算法,从冒泡排序中产生一个无向图。

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

//输入:点数n,1到n的全排列a 
//输出:一个点数为n的无向图G

void bubblesortgraph(n,a[]) {
	创建一个有n个点,0条边的无向图G。
    do { 
        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
            }
    } while(swapped);
    输出图G。
} //结束。

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

a是全排列。

\(1\le N\le 10^5.\)

讲解

数据太大,我们不能把图建出来,所以直接考虑哪些点之间没有边。

直接可以得到结论是上升子序列,所以第一问的答案就是最长上升子序列的长度。

第二问直接正反各跑一边即可。

时间复杂度 \(O(n\log_2n)\)

代码

精简
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 100005;
int n,ans;
int a[MAXN],pre[MAXN],suf[MAXN],dp[MAXN],vis[MAXN];

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read();
	for(int i = 1;i <= n;++ i) a[i] = Read();
	int cnt = 0;
	for(int i = 1;i <= n;++ i)
	{
		int to = lower_bound(dp+1,dp+cnt+1,a[i])-dp;
		pre[i] = to; dp[to] = a[i];
		cnt = Max(cnt,to);
	}
	ans = cnt; cnt = 0;
	for(int i = n;i >= 1;-- i)
	{
		a[i] = n-a[i]+1;
		int to = lower_bound(dp+1,dp+cnt+1,a[i])-dp;
		suf[i] = to; dp[to] = a[i];
		cnt = Max(cnt,to);
	}
	Put(ans,'\n');
	for(int i = 1;i <= n;++ i)
		if(pre[i]+suf[i] > ans) ++vis[pre[i]];
	for(int i = 1;i <= n;++ i)
		if(pre[i]+suf[i] > ans && vis[pre[i]] <= 1) Put(i,' ');
	return 0;
}
posted @ 2021-11-15 10:54  皮皮刘  阅读(53)  评论(0编辑  收藏  举报