• @codeforces



    @description@

    给定两个排列 p, q,他们中的有些位置被替换成了 0。

    两个排列 p, q 的距离为:最少需要在 p 中执行交换两个元素的操作,使得 p, q 相等。

    对于每个 0 <= k <= n,求有多少将 0 替换回正整数并满足 p, q 依然是排列的替换方法,使得 p, q 距离为 k。

    原题戳这里

    @solution@

    考虑两个排列的距离怎么算。如果把 p[i] -> q[i] 看成一个置换,就是将置换排序最少需要的交换次数。
    这是个经典问题,答案为 (n - 该置换含有的循环数量)。

    考虑一种特殊情况:如果给定完整的 p 而 q 全零,实际上就是求 n 个数划分成 n-k 个循环的方案数,即第一类斯特林数。
    因此本题跟第一类斯特林数或多或少有些勾连。

    通过 p, q 中已经给定的数,可以把现在已有的循环求出来。剩余的一定会形成若干条链。
    直接拿现有的链去作第一类斯特林数?貌似不行。考虑一个例子:p = {1, 0}, q = {0, 2},此时 1 与 2 不可能在同一循环。

    形象化地描述,假如 p[i] = a 且 q[i] = 0,可以理解为 a 的下面凸,否则认为 a 的下面凹;同理,假如有 p[j] = 0 且 q[j] = b,可以理解为 b 的上面凸,否则认为 b 的上面凹。
    则如果可以 a->b 应该满足 a 的下面与 b 的上面应该为一凹一凸。

    一个链中间的东西对链的转移没有限制,只有两个端点会产生限制。
    假如一个链形如 a -> ... -> b,则这条链上面的凹凸性取决 a,下面的凹凸性取决 b。
    因此一条链的限制可以描述为上下的凹凸性,我们可以将所有链分为 4 类。

    为了避免出现更多的分类讨论,我们假设 p[i] = 0, q[i] = 0 的 i 为上下都凸的链(理解成 p[i] = n + i, q[i] = 0 与 p[n + i] = 0, q[n + i] = n + i 也行)。

    那么问题转成了给定 4 类链的数量,求将这些链划分为 p 个循环的方案数。
    回到我们一开始的想法:这种题与第一类斯特林数有点关系。因此类比第一类斯特林数的递推式子进行 dp。

    斯特林数的递推式子:每次加入一个数,要么单独成一个循环,要么接在前面已经加入的某个数的后面。
    先把不是上下都凹的全部求第一类斯特林数。但可能会出现不合法情况:凸凸相接或凹凹相接。
    凸凸相接直接把上下都凹的当作润滑剂塞进去,凹凹相接没办法,只能在 dp 中限制。
    所以 dp 时不让上凹下凸的接在上凸下凹的后面即可。其他和斯特林数一样。

    时间复杂度貌似是 O(n^2)?

    @accepted code@

    #include <cstdio>
    const int MAXN = 250;
    const int MOD = 998244353;
    int f[MAXN + 5], g[MAXN + 5], c[4];
    void solve() {
    	int p = 0; f[0] = 1;
    	for(int i=1;i<=c[0];i++) f[0] = 1LL*f[0]*i%MOD;
    	for(int i=1;i<=c[3];i++) {
    		for(int j=0;j<=p;j++) g[j] = f[j], f[j] = 0;
    		p++;
    		for(int j=1;j<=p;j++) f[j] = (1LL*g[j]*(i-1)%MOD + g[j-1]) % MOD;
    	}
    	for(int i=1;i<=c[2];i++) {
    		for(int j=0;j<=p;j++) g[j] = f[j], f[j] = 0;
    		p++;
    		for(int j=1;j<=p;j++) f[j] = (1LL*g[j]*(c[3]+i-1)%MOD + g[j-1]) % MOD;
    	}
    	for(int i=1;i<=c[1];i++) {
    		for(int j=0;j<=p;j++) g[j] = f[j], f[j] = 0;
    		p++;
    		for(int j=1;j<=p;j++) f[j] = (1LL*g[j]*(c[3]+i-1)%MOD + g[j-1]) % MOD;
    	}
    }
    int a[MAXN + 5], b[MAXN + 5], n;
    int u[MAXN + 5], d[MAXN + 5], p[MAXN + 5];
    bool vis[MAXN + 5];
    int main() {
    	scanf("%d", &n);
    	for(int i=1;i<=n;i++) scanf("%d", &a[i]), u[a[i]] = i;
    	for(int i=1;i<=n;i++) scanf("%d", &b[i]), d[b[i]] = i;
    	for(int i=1;i<=n;i++)
    		if( u[i] ) p[i] = b[u[i]];
    	for(int i=1;i<=n;i++)
    		if( !d[i] || !a[d[i]] ) {
    			int x = i;
    			while( p[x] )
    				vis[x] = true, x = p[x];
    			vis[x] = true;
    			if( !d[i] ) {
    				if( !u[x] ) c[0]++;
    				else c[1]++;
    			}
    			else {
    				if( !u[x] ) c[2]++;
    				else c[3]++;
    			}
    		}
    	int cnt = 0;
    	for(int i=1;i<=n;i++) {
    		if( vis[i] ) continue;
    		int x = i;
    		do {
    			vis[x] = true;
    			x = p[x];
    		}while( x != i );
    		cnt++;
    	}
    	for(int i=1;i<=n;i++)
    		if( !a[i] && !b[i] ) c[3]++;
    	solve();
    	for(int i=n;i>=cnt&&i>=1;i--)
    		printf("%d ", f[i-cnt]);	
    	for(int i=cnt-1;i>=1;i--) printf("%d ", 0);
    }
    

    @details@

    这种题难度值3400?貌似官方给的是 fft 做法?

    话说我也不知道我是怎么想到这么“形象”的理解的。
    感觉“形象”到有点抽象了。

  • 相关阅读:
    NoSQL数据库:Redis内存使用优化与存储
    http缓存与cdn相关技术
    sphinx 全文索引
    Elasticsearch7.1中文文档-第一章-入门
    创建一个新的laravel
    mysql数据库的主从同步,实现读写分离
    使用 Swoole 来加速你的 Laravel 应用
    Linux下全局安装composer方法
    Laravel 加载第三方类库的方法
    jquery里把数组转换成json的方法
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12020740.html
Copyright © 2020-2023  润新知