冒泡排序

2020.10.4

题目描述

有一个 \(n\) 个点的图 \(G\) ,最开始没有边。现有一个 \(1~n\) 的排列,若对于一组 \(i,j\in[1,n]\)\(a_i>a_j\) 则在图 \(G\) 中连一条 \(i\leftrightarrow j\) 的无向边。求图 \(G\) 的最大独立集大小。其中 \(1\leq n\leq 10^5\)

解法

反图:图 \(G\) 在完全图上的补集

独立集:集合中任意两个点间都没有边相连的集合。

团:集合中任意两个点间都有边相连的集合。

最大独立集/团:集合元素个数最多的独立集/团。

性质:无向图的最大独立集等于其反图的最大团。

证明:显然,在反图中最大团里的点在原图中都没有边相互连接,则这些点即为原图的最大独立集。

回到此题,若其原图中有连边 \(i\leftrightarrow j\) 则在序列中,\(a_i\)\(a_j\) 一定为逆序对。反过来,如果在反图中有连边则在序列中为顺序对。而最大团中的点都相互有连边,则这些点共同组成了一个上升子序列。那么最大团大小即为最长上升子序列的长度。又有此序列为一个排列,那么显然可以直接用树状数组维护。

#include<stdio.h>
#define N 100007

int c[N],n;
inline int max(int x,int y){return x>y? x:y;}
inline int lowbit(int x){return x&(-x);}
inline void add(int x,int v){while(x<=n){c[x]=max(c[x],v);x+=lowbit(x);}}
inline int query(int x){int ret=0;while(x){ret=max(ret,c[x]);x-=lowbit(x);}return ret;}

inline int read(){
    int x=0,flag=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

int f[N],a[N];
int main(){
    freopen("sort.in","r",stdin);
    freopen("sort.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) a[i]=read();
    int ans=0;
    for(int i=1;i<=n;i++){
        f[i]=query(a[i])+1;
        add(a[i],f[i]);
        ans=max(ans,f[i]);
    }
    printf("%d",ans);
}

/*
3
3 1 2

5
4 1 5 3 2
*/
原文地址:https://www.cnblogs.com/wwlwQWQ/p/13767384.html