zoukankan      html  css  js  c++  java
  • 【BZOJ4715】囚人的旋律 DP

    【BZOJ4715】囚人的旋律

    Description

    我们令a[i]表示看守的第i扇门对应囚犯的哪一扇门。令图G为有n个节点的图,编号为1~n。对于满足1≤i<j≤n的一对i和j,如果有a[i]>a[j],那么在G中编号为i和j的节点之间连一条边。得到的图G被称为逆序图。对于图G=(V,E),非空点集S∈V是一个独立集当且仅当对于任意两个点u,v∈V,不存在(u,v)∈E。而S是一个覆盖集当且仅当对于任意点v?S存在点u∈S满足(u,v)∈E。我们在意的是,图G中有多少个点集既是独立集又是覆盖集。出于某种不知名的原因,被迫参加监狱游戏的大家的安危和这个问题的答案有关。拜托了,请一定要求出这个方案数。

    Input

    输入第一行含有两个整数n和m,表示逆序图的点数和边数。
    接下来m行,每行描述一条边。每行包含两个1~n的整数,代表边的两个端点。保证没有重边和自环。
    保证给定的图是一个合法的逆序图,即,存在至少一个序列,按照题目描述中所述方法得到的逆序图是给定的图。
    n≤1000,0≤m≤(n(n-1))/2

    Output

    输出一个整数,表示方案数对1,000,000,007取模得到的结果。

    Sample Input

    5 5
    2 4
    2 5
    1 4
    3 4
    3 5

    Sample Output

    3

    题解:首先,根据这个逆序图是可以在O(n^2)时间内将原序列还原出来的。

    方法:统计每个数前面有多少个大于它的数pre,后面有多少个小于它的数nxt,那么最小的数i一定满足pre[i]=i-1&&nxt[i]=0,所以找出最小的数,然后将它删掉,更新其它点的pre和nxt。不断重复此过程即可。

    那么本题求的东西到底是什么呢?一开始以为求的是边覆盖集,然后就变成了将原序列分成两个不相交的上升子序列,但是样例过不去。。。后来发现求的是点覆盖集。。。

    因为选出来的序列是独立集,所以这个序列是递增的;又因为这是覆盖集,所以每个不在序列中的点都与某个序列中的点形成逆序对。所以我们求的就是极长上升序列的方案数!用f[i]表示前i个数中极长上升序列方案数,如果i是一个合法的结尾,则用f[i]更新答案即可。

     

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int P=1000000007;
    int n,m,ans;
    int map[1010][1010],f[1010],nxt[1010],pre[1010],v[1010],bef[1010],aft[1010];
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),m=rd();
    	int i,j,k,a,b;
    	for(i=1;i<=m;i++)
    	{
    		a=rd(),b=rd();
    		if(a>b)	swap(a,b);
    		map[a][b]=1,nxt[a]++,pre[b]++;
    	}
    	for(i=1;i<=n;i++)	for(j=i+1;j<=n;j++)
    	{
    		if(!map[i][j])	bef[j]=aft[i]=1;
    	}
    	for(i=1;i<=n;i++)
    	{
    		for(k=1;k<=n;k++)	if(!v[k]&&!nxt[k]&&pre[k]==k-1)	break;
    		v[k]=i;
    		for(j=1;j<k;j++)	if(map[j][k])	nxt[j]--;
    		for(j=k+1;j<=n;j++)	pre[j]++;
    	}
    	for(i=1;i<=n;i++)
    	{
    		f[i]=(f[i]+(!bef[i]))%P;
    		for(k=n+1,j=i+1;j<=n;j++)	if(!map[i][j]&&v[j]<k)	k=v[j],f[j]=(f[j]+f[i])%P;
    		if(!aft[i])	ans=(ans+f[i])%P;
    	}
    	printf("%d",ans);
    	return 0;
    }

     

  • 相关阅读:
    SAP接口编程 之 JCo3.0系列(03) : Table参数
    SAP接口编程 之 JCo3.0系列(01):JCoDestination
    SAP接口编程 之 JCo3.0系列(02) : JCo Client Programming
    ORACLE SQL 分组
    Win10无法安装提示磁盘布局不受UEFI固件支持怎样解决
    win10系统更新补丁时进度条一直卡在0%不动的解决方案
    SAP 批量查看凭证更改记录
    Oracle 中取当前日期的上个月最后天和第一天
    table 谷歌下不出现滚动条
    style.display table-row与block
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7634269.html
Copyright © 2011-2022 走看看