1 条题解

  • 0
    @ 2025-8-24 22:14:59

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 7KByte
    **

    搬运于2025-08-24 22:14:59,当前版本为作者最后更新于2022-02-11 15:04:40,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    更好的体验尽在我的博客

    不用生成函数,目前比 rank2 快四倍。

    首先这是一道不简单的数数思维题。

    我们要统计对于所有排列的深度之和,直接做非常不方便。而数数题一般将条件化简,或找到等价的容易处理的条件。

    这里求深度,等价于枚举一个点的祖先,它的祖先个数 +1+1 就是它的深度。这样问题转化为求数对 (u,v)(u,v) 的贡献,即有多少排列使得 vvuu 的祖先。

    如果 vvuu 的祖先,等价于 av<aua_v < a_u,且 u,vu,v 之间没有小于 ava_v 的数。

    如果不考虑其它限制条件,先求有多少个排列恰好有 KK 个逆序对。这是一个经典题目,直接定义状态 fi,jf_{i,j} 表示长度为 ii 的逆序对数为 jj 的排列个数。枚举最后一位的 ii 种选择,分别是逆序对增加 x[0,i1]x\in[0,i - 1] 个,典型的背包模型,可以前缀和优化至 O(N3)\mathcal{O}(N^3)

    现在有 (u,v)(u,v) 的限制条件,我们可以先固定 u,vu,v 之间的数,再固定 u,vu,v,最后固定外面的数。不难发现除了位置 vv,其余的数可以随便填,和上面的转移一模一样。对于位置 vv,如果在 uu 后面,一定会产生 vuv-u 个逆序对,否则一定不产生逆序对。

    所以我们先跑 DP 求出 ff。然后枚举 vuv-u,然后在背包中撤销 (vu)(v-u),撤销后的 fn1,Kmax(0,vu)f_{n - 1,K-\max(0,v-u)} 就是我们要求的贡献。

    时间复杂度 O(N3)\mathcal{O}(N^3),代码没有一次乘法和取模,跑的飞快。

    #define M 46600
    #define N 305
    int n, k, f[2][M], s[M], g[M], sz, ans[N];
    #define ck(x) (x >= P ? x - P : x)
    void undo(int x){
    	memset(g, 0, sizeof(g));
    	int cur = P - 1; g[0] = 1;
    	rp(i, sz - x){
    		if(i > x)ad(cur, g[i - x - 1]);
    		su(cur, g[i] = ck(f[n & 1][i] + cur));
    	}
    }
    int main() {
    	read(n, k, P);
    	f[0][0] = 1;
    	rp(i, n){
    		s[0] = f[i & 1][0] = 1, sz += i - 1;
    		rp(j, sz){
    			f[i & 1][j] = s[j] = ck(s[j - 1] + f[1 & (i - 1)][j]);
    			if(j >= i)su(f[i & 1][j], s[j - i]);
    		}
    	}
    	rp(i, n)ans[i] = f[n & 1][k];
    	rp(t, n - 1){
    		undo(t);
    		rp(i, n - t){
    			if(t <= k)ad(ans[i], g[k - t]);
    			ad(ans[i + t], g[k]);
    		}
    	}
    	rp(i, n)printf("%d ", ans[i]); el;
    	return 0;
    }
    
    
    • 1

    信息

    ID
    4847
    时间
    1000ms
    内存
    125MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者