CodeForces Gym 100228 Graph of Inversions
阿新 • • 發佈:2018-10-05
!= 定義 stream i++ pac 不難 string fin style
題目大意
對於一個長為$N$的序列$A$,定義它所對應的逆序圖:
有$N$個點組成,標號為$1...N$的無向圖,對於每一組$i,j(i<j)$若存在$A_i>A_j$則在新圖中就存在一條$(A_i,A_j)$的無向邊。
現在給定一個$N(N\leq 1000)$個點的圖,保證它是某個序列對應的逆序圖,求它有多少個點集$S$,滿足$\forall x\in S,y\in S$不存在邊$(x,y)$,$\forall x\notin S$至少存在一條邊$(x,y)$使得$y\in S$。即求圖獨立覆蓋集的數量。
題解
不難發現這個序列可以的轉化為一個排列。
我們可以用拓撲排序整理每對數的大小關系,從而在序列上考慮這個問題。
對於第一個條件,即點集內兩兩沒有連邊,就是滿足在序列上點集對應的位置恰好形成一個上升的子序列。
對於第二個條件,滿足對於$\forall x\notin S$一定至少存在一個$y\in S$使得$A_x<A_y,x>y$或$A_y<A_x,x<y$。
假設$x\notin S,y\in S$,由於$\{A_y\}$為上升子序列,和$A_x$組成逆序對的是左側最大的$A_x$和右側最小的$A_x$,那麽就是$A_y<A_x(x=max\{x\in S,x<y\})$或$A_y>A_x(x=\min\{x\in S,x>y\})$,即對於$i,j\in S,A_i$不存在$k$使得$i<k<j,A_i<A_k<A_j,k\notin S$這樣就很顯然了,直接$Dp$,設$F_i$表示最後一個是$i$前$i$個合法的方案數。
暴力轉移即可。復雜度$O(n^2)$。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define M 1020 using namespace std; LL read(){ LL nm=0,fh=1; char cw=getchar(); for(;!isdigit(cw);cw=getchar()) if(cw==‘-‘) fh=-fh; for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-‘0‘); return nm*fh; } LL n,m,fs[M],nt[M*M],to[M*M],tmp,ind[M],p[M],cnt,tot,q[M],hd,tl; LL mp[M][M],F[M],G[M],ans; void link(LL x,LL y){nt[tmp]=fs[x],fs[x]=tmp,to[tmp++]=y,ind[y]++;} int main(){ n=read(),m=read(),memset(fs,-1,sizeof(fs)); while(m--) mp[read()+1][read()+1]=1; for(LL i=1;i<=n;i++){ for(LL j=i+1;j<=n;j++) if(mp[i][j]||mp[j][i]) link(j,i);else link(i,j); } for(LL i=1;i<=n;i++) if(!ind[i]) q[tl++]=i; while(hd<tl){ LL x=q[hd++]; p[x]=++cnt; for(LL i=fs[x];i!=-1;i=nt[i]) if(!(--ind[to[i]])) q[tl++]=to[i]; } F[0]=1; for(LL i=0;i<n;i++){ LL minn=n+2; for(LL j=i+1;j<=n;j++){ if(p[j]>minn||p[j]<p[i]) continue; F[j]+=F[i],minn=p[j]; } } for(LL i=n,maxn=0;i;i--) if(p[i]>maxn) ans+=F[i],maxn=p[i]; printf("%lld\n",ans); return 0; }
CodeForces Gym 100228 Graph of Inversions