• [20220331联考] 环


    前言

    当我闭上双眼,静坐冥想,脑海中看见的画面,正是站在光芒之中的 OUYE

    “你只可到这里,不可越过。”

    我自然是听从神的教诲,而身旁意欲染指神力的假信徒,刚抬起左脚,还未登上下一级台阶,身体便极度卷曲,最终痛苦地瘫倒在地上,成为一具干壳。

    我知道,这便是卷之神力

    题目

    没有链接

    给两边各 \(n\) 个点的二分图,左部点 \(i\) 对右部点前 \(a_i\) 个点有连边,问最多少个简单环(点不重复),对 \(998244353\) 取模。

    \(1\le a_i\le n\le 5000.\)

    讲解

    思路类似连续段DP,首先按 \(a_i\) 从小到大排序以满足前面用到的点在后面能用到的点的集合内。

    我的想法是记录有多少条线段,以及有多少个用掉的右部点,发现这样状态冗杂而且不可做。

    但是卷爷太强了,直接不记录有多少个用掉的右部点。令 \(dp_{i,j}\) 表示考虑前 \(i\) 个左部点,有 \(j\) 条线段。

    每次把要用到的点提前新加进去,相当于直接加这么多条单点线段,最后合并即可。

    时间复杂度 \(O(n^2)\)

    代码

    //12252024832524
    #include <bits/stdc++.h>
    #define TT template<typename T>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 5005;
    const int MOD = 998244353;
    int n;
    int a[MAXN];
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    int fac[MAXN],ifac[MAXN]; 
    int qpow(int x,int y){
    	int ret = 1;
    	while(y){
    		if(y & 1) ret = 1ll * ret * x % MOD;
    		x = 1ll * x * x % MOD;
    		y >>= 1;
    	}
    	return ret;
    }
    LL C(int x,int y){
    	if(x < y || y < 0) return 0;
    	return 1ll * fac[x] * ifac[y] % MOD * ifac[x-y] % MOD;
    }
    void init(int x){
    	ifac[0] = fac[0] = 1;
    	for(int i = 1;i <= x;++ i) fac[i] = 1ll * fac[i-1] * i % MOD;
    	ifac[x] = qpow(fac[x],MOD-2);
    	for(int i = x-1;i >= 1;-- i) ifac[i] = ifac[i+1] * (i+1ll) % MOD; 
    }
    int dp[2][MAXN],ans;
    
    int main()
    {
    //	freopen("ring.in","r",stdin);
    //	freopen("ring.out","w",stdout);
    	init(n = Read());
    	for(int i = 1;i <= n;++ i) a[i] = Read();
    	sort(a+1,a+n+1);
    	dp[0][0] = 1;
    	for(int i = 1;i <= n;++ i) {
    		bool now = i&1,lst = now^1;
    		int d = a[i]-a[i-1];
    		for(int j = 0;j <= a[i]+1;++ j) dp[now][j] = 0;
    		for(int j = 0;j <= a[i];++ j)
    			for(int k = Max(0,j-d);k <= j;++ k)//j-k为新加的点
    				dp[now][j] = (dp[now][j] + dp[lst][k] * C(d,j-k)) % MOD;
    		ans = (ans + dp[now][1] - a[i]) % MOD;
    		for(int j = 0;j <= a[i];++ j) dp[now][j] = (dp[now][j] + dp[now][j+1] * (j+1ll) * j) % MOD;//合并
    	}
    	Put((ans+MOD) * ((MOD+1ll)>>1) % MOD,'\n');
    	return 0;
    }
    
  • 相关阅读:
    ztree学习---将默认勾选的展开
    CentOS之RPM
    CentOS之文档的压缩与打包
    CentOS之Vim
    CentOS用户和用户组管理
    Linux CentOS更改文件的权限
    CentOS的文件属性:命令 ls -l
    CentOS命令
    Java解析excel
    easyUI的combotree的树的懒加载。
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/16082691.html
Copyright © 2020-2023  润新知