• 洛谷 P7154


    Portal

    题意:

    给出两个序列 (a_1,a_2,dots,a_n)(b_1,b_2,dots,b_n)(i)(j) 能匹配当且仅当 (a_ileq b_j)
    定义一个匹配是极大的,当且仅当对于匹配的 ((i,j)) 都有 (a_ileq b_j) 并且未被匹配的 (a_i,b_j) 两两无法匹配。
    求极大匹配的个数 (mod(10^9+7))
    (1leq nleq 3000)

    真·Pt 三个 (mod (10^9+7))
    真·去年 Pt bao0 今年还是 bao0
    我还有什么好说的呢?/kk

    现场只会 (n^3) 的做法,赛后看了题解才发现我们做法里正解只差一步之遥。
    显然,一个匹配是极大匹配的充要条件是最小的未被匹配的 (a_i) (gt) 最大的未被匹配的 (b_j)
    (a,b) 的下标对匹配个数显然是没有影响的,故不管三七二十一先将 (a,b) 从小到大排个序。
    定义 (p_j) 为最大的满足 (a_ileq b_j)(i)。那么 (j) 能匹配的 (a_i)(a) 的一段前缀——一段长度为 (p_j) 的前缀。那么这样一来,每个 (b_j) 有两种选择,一种是选择一个下标 (leq p_j) 的,被钦定“要被匹配”却还没有被匹配的 (a_i) 并与其进行匹配。一种是干脆不匹配。但是,如果在 (1)~(p_j) 中存在某个 (a_i) 被钦定“不被匹配”,那么就不能选择第二种情况。因为这样一来就会存在一个未被匹配的 (a_ileq b_j),不符合极大匹配的条件。
    我们可以因此设计出 (dp) 状态:(dp_{i,x,f}) 表示我们在前 (p_i)(a_i) 和前 (i)(b_j) 中匹配 ,现在有 (x)(a_i) 被钦定“要被匹配”却还没有被匹配,(f) 表示是否存在某个 (a_i) 被钦定“不被匹配”。考虑 ((p_i,p_{i+1}]) 中的 (a_i),我们枚举其当中被钦定“要被匹配”的个数 (k),然后可以得到下面两个转移方程式:

    • (dp_{i+1,x+k-1,f|(k!=(p_{i+1}-p_i))}+=dp_{i,j,k} imes inom{p_{i+1}-p_i}{k} imes(x+k))
    • (dp_{i+1,x+k,f|(k!=(p_{i+1}-p_i))}+=dp_{i,j,k} imes inom{p_{i+1}-p_i}{k}) 这种转移要求 (f|(k!=(p_{i+1}-p_i))=0)

    稍微解释一下这两个转移方程:第一个方程对应上面的第一种情况,从 (p_{i+1}-p_i)(a_i) 当中钦定选择 (k) 个的方案数为 (inom{p_{i+1}-p_i}{k}),再选择一个与 (b_{i+1}) 匹配有 (x+k) 种选法。第二个方程对应上面的第二种情况,即 (b_{i+1}) 不与任何 (a_i) 匹配。但这种转移有个前提条件,那就是 ([1,p_{i+1}]) 中所有的 (a_i) 都要被钦定匹配,即 (f|(k!=(p_{i+1}-p_i))=0)
    最终答案即为 (dp_{n,0,0}+dp_{n,0,1})。时间复杂度乍一看是 (n^3) 的,但 (sum p_{i+1}-p_i=n),故转移均摊 (mathcal O(1)),总复杂度 (mathcal O(n^2))

    看到没?什么超纲的算法都没有。所以啊,菜是原罪/kk

    #include <bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define fz(i,a,b) for(int i=a;i<=b;i++)
    #define fd(i,a,b) for(int i=a;i>=b;i--)
    #define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
    #define fill0(a) memset(a,0,sizeof(a))
    #define fill1(a) memset(a,-1,sizeof(a))
    #define fillbig(a) memset(a,63,sizeof(a))
    #define pb push_back
    #define ppb pop_back
    #define mp make_pair
    template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
    template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
    typedef pair<int,int> pii;
    typedef long long ll;
    template<typename T> void read(T &x){
    	x=0;char c=getchar();T neg=1;
    	while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
    	while(isdigit(c)) x=x*10+c-'0',c=getchar();
    	x*=neg;
    }
    const int MAXN=3000;
    const int MOD=1e9+7;
    int n,a[MAXN+5],b[MAXN+5],p[MAXN+5],dp[MAXN+5][MAXN+5][2],c[MAXN+5][MAXN+5];
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]);sort(a+1,a+n+1);
    	for(int i=1;i<=n;i++) scanf("%d",&b[i]);sort(b+1,b+n+1);
    	for(int i=0;i<=MAXN;i++){
    		c[i][0]=1;
    		for(int j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
    	}
    	int pos=1;
    	for(int i=1;i<=n;i++){
    		while(pos<=n&&a[pos]<=b[i]) pos++;
    		p[i]=--pos;
    	}
    	dp[0][0][0]=1;
    	for(int i=0;i<n;i++) for(int j=0;j<=p[i];j++){
    		for(int k=0;k<2;k++) for(int l=0;l<=p[i+1]-p[i];l++){
    			int nk=(k||(l!=(p[i+1]-p[i])));
    			if(j+l-1>=0) dp[i+1][j+l-1][nk]=(dp[i+1][j+l-1][nk]+1ll*dp[i][j][k]*(j+l)%MOD*c[p[i+1]-p[i]][l]%MOD)%MOD;
    			if(!nk) dp[i+1][j+l][nk]=(dp[i+1][j+l][nk]+1ll*dp[i][j][k]*c[p[i+1]-p[i]][l])%MOD;
    		}
    	}
    //	for(int i=1;i<=n;i++) for(int j=0;j<=p[i];j++) for(int k=0;k<2;k++)
    //		printf("%d %d %d %d
    ",i,j,k,dp[i][j][k]);
    	printf("%d
    ",(dp[n][0][0]+dp[n][0][1])%MOD);
    	return 0;
    }
    
  • 相关阅读:
    DockerAPI版本不匹配的问题
    Linux文件系统
    队列

    多维数组
    字符串
    线性表
    ARM编辑、编译工具
    南京IT公司
    数据结构:用单链表实现的队列(2)
  • 原文地址:https://www.cnblogs.com/ET2006/p/luogu-P7154.html
Copyright © 2020-2023  润新知