學校健康系統自動打卡
阿新 • • 發佈:2020-10-04
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 */