zoukankan      html  css  js  c++  java
  • [ABC217]F

    [ABC217]F - Make Pair

    题目

    (2N)个学生,编号(1,2,3ldots 2N),其中有(M)对学生关系友好,教师每次可以从中选出一对相邻的学生,且他们关系友好,然后将这对学生删除(注意删除后学生的相邻关系改变).

    求出删除所有学生的方案数,取模.

    思路

    这种问方案数,还取模的题基本都是DP.看下数据范围大概在(O(n^3))左右.

    应该不难想到区间DP.

    (f_{i,j})表示消掉(isim j)所有学生的方案数.考虑如何从小区间推到大区间,我们把第(i)个人单独拿出来,枚举(kin [i+1, j]),如果第(i)个人和第(k)个人关系友好,我们可以先合并(i+1sim k-1)的人,然后合并(i,k)(k+1sim j)(后面这两个顺序是可以换的).为了方便,令(n_1=k-i+1),(n_2=j-k)(两边的人数),产生的贡献就是

    [f_{i,k}cdot f_{k+1,j}cdot C^{n_2}_{n_1+n_2} ]

    这是一个经典的组合数问题.

    我们称(isim k)为左半边,(k+1sim j)为右半边.

    设合并左半边的操作序列为(A_1,A_2,ldots ,A_{n_1}),合并右半边的操作序列为(B_1,B_2,ldots ,B_{n_2}),显然,合并(isim j)(A,B)原来的操作顺序不能改变,即对于所有的(iin [2,n_1]),(A_{i-1})要在(A_i)之前出现,对于(B)也同理.

    原问题等价于从(n_1+n_2)个小球中选出(n_1)个球的方案数,这样理解:所有小球等价于合并全区间的操作序列, 选出的小球代表左半边的操作序列,剩下的代表右半边的操作序列,比如,6个小球中,选出第(1,2,4,6)个,对应的合并后的操作序列就是(A_1,A_2,B_1,A_3,B_2,A_4).

    因此,合并(isim j)所有人的操作方案数就是(C^{n_2}_{n_1+n_2}).

    最后,答案显然就是(f_{1,2n}).

    代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    typedef long long lint;
    const int N = 410;
    const lint mod = 998244353;
    int n , m;
    int rel[N][N];
    lint f[N][N];
    
    lint fac[N];
    lint inv[N];
    
    lint pow_(lint a , int p) {
    	lint res = 1;
    	while(p) {
    		if(p & 1)
    			res = res * a % mod;
    		a = a * a % mod;
    		p >>= 1;
    	}
    	return res;
    }
    signed main() {
    	cin >> n >> m;
    	n *= 2;
    	for(int i = 1 ; i <= m ; i++) {
    		int u , v;
    		cin >> u >> v;
    		rel[u][v] = rel[v][u] = 1;
    		if(u == v + 1 || v == u + 1)
    			f[u][v] = f[v][u] = 1;
    	}
    
    	fac[0] = 1;
    	for(int i = 1 ; i <= n ; i++)
    		fac[i] = fac[i - 1] * i % mod;
    	for(int i = 1 ; i <= n ; i++)
    		inv[i] = pow_(fac[i] , mod - 2);
    
    	for(int i = n ; i > 0 ; i--)
    		for(int j = i + 3 ; j <= n ; j+=2) {
    			if(j - i + 1 & 1)
    				continue;
    			if(rel[i][j])
    				f[i][j] += f[i + 1][j - 1];
    			for(int k = i + 1 ; k < j ; k += 2)
    				if(rel[i][k])
    					f[i][j] += f[i + 1][k - 1] * f[k + 1][j] % mod
    					           * fac[j - i + 1 >> 1] % mod
    					           * inv[j - k >> 1] % mod
    					           * inv[j - i + 1 - (j - k) >> 1] % mod ,
    					f[i][j] %= mod;
    		}
    	cout << f[1][n];
    	return 0;
    }
    
  • 相关阅读:
    CSU 1333 Funny Car Racing
    FZU 2195 检查站点
    FZU 2193 So Hard
    ZOJ 1655 FZU 1125 Transport Goods
    zoj 2750 Idiomatic Phrases Game
    hdu 1874 畅通工程续
    hdu 2489 Minimal Ratio Tree
    hdu 3398 String
    洛谷 P2158 [SDOI2008]仪仗队 解题报告
    POJ 1958 Strange Towers of Hanoi 解题报告
  • 原文地址:https://www.cnblogs.com/dream1024/p/15244203.html
Copyright © 2011-2022 走看看