• LOJ


    题目

    传送门

    解法

    ( ext{40 pts})

    从小到大放置 (n) 个数,对于第 (i) 个数就有 (i) 个位置放置,贡献值域为 ([0,i-1])

    (dp_{i,j}) 为前 (i) 个数形成 (j) 个逆序对的方案数。就有:

    [dp_{i,j}=sum_{k=0}^{i-1} dp_{i-1,j-k} ]

    ( ext{60 pts})

    [dp_{i,j}-dp_{i,j-1}=dp_{i-1,j}-dp_{i-1,j-i} ]

    ( ext{100 pts})

    问题可以转换成这样的形式:求满足

    [sum_{i=1}^n x_i=k,x_ile i-1 ]

    的解的方案数。

    可以钦定 (m)(x_i) 不满足性质,设这些 (x_i)(i) 之和为 (s)(x_i) 值为 (i) 恰好不满足性质)。那么方案数就是 (inom{n-1}{k+n-1-s}),容斥系数是 ((-1)^m)

    这显然过不去,但其实我们并不关心是哪些 (x_i) 超过限制,我们关心的是 (s)(m)

    那么令 (dp_{i,j}) 为选 (i) 个数,它们的和为 (j) 的方案数。由于这 (i) 个数 互异,可以这样转移:

    • 将所选数整体 (+1)(dp_{i,j}=dp_{i,j-i})
    • 将所选数整体 (+1),再加入一个值为 (1) 的数。(dp_{i,j}=dp_{i,j}+dp_{i-1,j-i})
    • 有数字加出了 (n),需要减去。(dp_{i,j}=dp_{i,j}-dp_{i-1,j-(n+1)})

    现在就是 (mathcal O(n^2)) 的咯?实际上,由于 (k) 的限制,我们并不需要选这么多数。考虑最节省的情况就是连续的等差数列,设项数为 (x)

    [frac{(1+x)cdot x}{2}=k ]

    这样 (x) 大概是 (sqrt k) 级别的。总复杂度 (mathcal O(nsqrt n))

    代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f|=(s=='-');
    	while(s>='0' and s<='9')
    		x=(x<<1)+(x<<3)+(s^48),
    		s=getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-'),write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    #include <cmath>
    
    const int mod=1e9+7,maxn=1e5+5;
    
    int n,k,fac[maxn<<1],ifac[maxn<<1];
    int dp[600][maxn];
    
    int qkpow(int x,int y) {
    	int r=1;
    	while(y) {
    		if(y&1) r=1ll*r*x%mod;
    		x=1ll*x*x%mod; y>>=1;
    	}
    	return r;
    }
    
    void init() {
    	fac[0]=1;
    	for(int i=1;i<=n+k;++i)
    		fac[i]=1ll*fac[i-1]*i%mod;
    	ifac[n+k]=qkpow(fac[n+k],mod-2);
    	for(int i=n+k-1;i>=0;--i)
    		ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
    }
    
    int C(int n,int m) {
    	if(n<m or n<0 or m<0)
    		return 0;
    	return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
    }
    
    int main() {
    	n=read(9),k=read(9); init();
    	int m=sqrt(k*2)+5;
    	dp[0][0]=1;
    	for(int i=1;i<=m;++i)
    		for(int j=i;j<=k;++j) {
    			dp[i][j]=(dp[i][j-i]+dp[i-1][j-i])%mod;
    			if(j>=n+1)
    				dp[i][j]=(dp[i][j]-dp[i-1][j-(n+1)]+mod)%mod;
    		}
    	int ans=C(n+k-1,n-1);
    	for(int i=1;i<=k;++i) {
    		int tmp=0;
    		for(int j=1;j<=m and j<=n;++j)
    			if(j&1) tmp=(tmp-dp[j][i]+mod)%mod;
    			else tmp=(tmp+dp[j][i])%mod;
    		ans=(ans+1ll*C(k+n-1-i,n-1)*tmp%mod)%mod;
    	}
    	print(ans,'
    ');
    	return 0;
    }
    
  • 相关阅读:
    一个标准的类通常要拥有下面四个组成部分
    局部变量和成员变量的区别
    Java学习: 面向对象的使用与注意事项
    学习:内存的申请与释放
    虚拟内存与物理内存
    实现:win32实现EDIT控件读取和保存
    学习:生产者和消费者模式实现线程同步
    学习:线程互斥
    学习:多线程
    实现:服务程序增删
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15120493.html
Copyright © 2020-2023  润新知