「重庆市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;
}