首页 > 代码库 > [HAOI2017模拟]囚人的旋律

[HAOI2017模拟]囚人的旋律

没有传送门辣。

 

神奇的DP题。

首先看到这道题第一眼应该想到正解不是在图上搞,肯定要把原图转化成序列。

根据逆序对的性质。每个点和标号大于他的点连边的点,其权值必定要小于该点,而没和他连边的且标号大于他的点,权值也必定大于他。

根据这个,我们就可以递增的枚举标号,每次判定该点和几个标号大于他的点连右边,即确定在未被选的权值里有多少个点比他小。

然后就可以确定当前点的标号了。

这样就成功的把原图转化成了序列。

 

接下来考虑在序列上做DP。

很显然,如果选中的一些点是满足要求的。必然要满足其构成一个上升子序列,只有上升子序列的点集才是题目所称的独立集。

覆盖集就不是那么好处理了。假设我们已经选定了一堆点,那么对于未选定的点,必定和其中某一个点构成逆序对。用数学公式表示一下就是这样的:

$\forall a_i \notin V_1$,必定$\exists a_j \in V_2$使得$a_i>a_j$且$i<j$或者$a_i<a_j$且$i>j$。

 

那么接下来考虑序列上的一个片段$(L,R)$,其中$L,R$都是已经被钦定的点。那么对于在$(L,R)$中的所有点$a_i$,一定不满足$a_L \leq a_i \leq a_R$。

但是不能存在一个点满足上述条件但是和之前的点存在逆序对关系吗?

考虑一个点满足上述条件。那么另一个被钦定的点要么大于$a_L$,要么小于$a_R$,这样就不满足独立集的关系了。

 

所以根据这个做DP即可。

即设$f[i]$表示以$i$结尾的方案数,加一些小优化即可满足$O(N^2)$的复杂度。

//senritsu//by Cydiater//2017.1.17#include <iostream>#include <queue>#include <map>#include <ctime>#include <cstring>#include <string>#include <iomanip>#include <ctime>#include <algorithm>#include <cstdlib>#include <cstdio>#include <bitset>#include <set>#include <vector>using namespace std;#define ll long long#define up(i,j,n)	for(int i=j;i<=n;i++)#define down(i,j,n)	for(int i=j;i>=n;i--)#define cmax(a,b)	a=max(a,b)#define cmin(a,b)	a=min(a,b)#define Auto(i,node)	for(int i=LINK[node];i;i=e[i].next)#define FILE 		"senritsu"const int MAXN=1e4+5;const int oo=0x3f3f3f3f;const int mod=1000000007;inline int read(){	char ch=getchar();int x=0,f=1;	while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)f=-1;ch=getchar();}	while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}	return x*f;}int N,M,LINK[MAXN],len=0,arr[MAXN],f[MAXN];bool used[MAXN];struct edge{	int y,next;}e[MAXN<<10];namespace solution{	inline void insert(int x,int y){		e[++len].next=LINK[x];LINK[x]=len;e[len].y=y;	}	inline void Insert(int x,int y){		insert(x,y);		insert(y,x);	}	void Prepare(){		N=read();M=read();		up(i,1,M){			int x=read(),y=read();			Insert(x,y);		}		up(i,1,N){			int cnt=0,K=0;			Auto(j,i)if(e[j].y>i)cnt++;			up(j,1,N)if(!used[j]){				K++;				if(K==cnt+1){					used[j]=1;					arr[i]=j;					break;				}			}		}		arr[0]=-oo;arr[N+1]=oo-1;	}	void Solve(){		int Min;		f[0]=1;		up(i,0,N+1){			Min=oo;			up(j,i+1,N+1){				if(arr[j]>arr[i]&&Min>arr[j])(f[j]+=f[i])%=mod;				if(arr[j]>arr[i])cmin(Min,arr[j]);			}		}		cout<<f[N+1]<<endl;	}}int main(){	freopen(FILE".in","r",stdin);	freopen(FILE".out","w",stdout);	using namespace solution;	Prepare();	Solve();	return 0;}

 

[HAOI2017模拟]囚人的旋律