• CF1349F1 Slime and Sequences


    一、题目

    点此看题

    二、解法

    下次再也不找这种阴间题做了,根本想不到好吗?

    首先做一个简单的转化:考虑让 (k-1) 第一次出现的位置大于 (k) 最后一次出现的位置。

    考虑构造映射去描述好序列,你发现转化后的条件是比较连贯的,因为 (k-1) 第一次出现的位置大于 (k) 最后一次出现的位置,而 (k) 第一次出现的位置大于 (k+1) 最后一次出现的位置。

    所以我们构建的映射要能很好的描述位置这一信息,那么考虑把 (1) 的出现位置从大到小写下,(2) 的出现位置从大到小写下(...)然后你发现写完这不是一个排列吗?那我们映射把好序列映射到一个排列上去了!

    再证明所有排列都能映射回一个好序列,我们把原排列划分成若干个极长下降子段,第一个子段就代表 (1) 的所有出现位置,第二个子段就代表 (2) 的所有出现位置(...)以此类推就能映射到好序列上去。

    那么得到结论:好序列能和排列一一映射


    剩下的问题就是计数了,不难看出这是一个贡献法,我们考虑在排列上计数,考虑前面有 (j-1) 个下降子段那么这个数就是 (j),那么我们考虑每个位置的贡献,把前面和后面分开,先选出 (i) 个数放在前面,后面的数随便排列,然后前 (i) 个数的方案中需要存在 (j-1)(a_i<a_{i+1})

    [ans_j=sum_{i=j}^n E(i,j-1)cdot {nchoose i}cdot (n-i)! ]

    这里补充一下前置芝士,(E(n,k)) 表示 (n) 个数的排列有 (k)(a_i<a_{i+1}) 的方案数,又称欧拉数,这个东西可以递推,考虑在 (n-1) 个数的基础上新插入一个 (n) 的影响:

    • 如果不会带来相邻顺序对的新增,那么可以插入在原来 (k) 对的空隙,或者插入到序列的开头,有 (k+1) 种方案,从 (E(n-1,k)) 处转移而来。
    • 如果会使相邻顺序对增加 (1),那么不能在原来 (k-1) 对的空隙中插入,也不能在序列的开头处插入,有 (n-k) 种方案,从 (E(n-1,k-1)) 处转移而来。

    所以 (E(n,k)=(k+1)cdot E(n-1,k)+(n-k)cdot E(n-1,k-1))

    三、总结

    计数中重要的方法是映射法,考虑原问题能不能映射到一些简单的模型中。此外,本题重要的是一个存在性限制,但不能转化所有性限制,转化为单点性限制就好做了很多。

    插入法是解决排列计数的重要方法,通常是插入新增的元素考虑它的影响。

    #include <cstdio>
    const int M = 5005;
    const int MOD = 998244353;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,o[M][M],fac[M],inv[M];
    void init()
    {
    	fac[0]=inv[0]=inv[1]=1;
    	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
    	for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
    	for(int i=2;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
    }
    signed main()
    {
    	n=read();init();
    	for(int i=1;i<=n;i++)
    		o[i][0]=1;
    	for(int i=2;i<=n;i++)
    		for(int j=1;j<i;j++)
    			o[i][j]=o[i-1][j]*(j+1)+o[i-1][j-1]*(i-j),o[i][j]%=MOD;
    	for(int j=1;j<=n;j++)
    	{
    		int ans=0;
    		for(int i=j;i<=n;i++)
    			ans=(ans+o[i][j-1]*fac[n]%MOD*inv[i])%MOD;
    		printf("%lld ",ans);
    	}
    }
    
  • 相关阅读:
    选择排序--简单(c语言)
    冒泡排序--简单(c++)
    冒泡排序--简单(c语言)
    直接插入排序--简单(c++)
    Microsoft Azure File 服务简介
    【 D3.js 选择集与数据详解 — 1 】 使用datum()绑定数据
    物联网操作系统HelloX V1.77(beta)版本发布
    对XX证券报关于物联网操作系统的几个问题的答复
    使用 Azure Site Recovery 将内部部署虚拟化工作负荷迁移至 Azure
    使用 Azure Site Recovery 灾难恢复至 Azure 的功能现已正式发布
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15058538.html
Copyright © 2020-2023  润新知