1. 程式人生 > >CodeForces Gym 100228 Graph of Inversions

CodeForces Gym 100228 Graph of Inversions

!= 定義 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