zoukankan      html  css  js  c++  java
  • Solution -「ARC 104D」Multiset Mean

    (mathcal{Description})

      Link.

      读题时间≈想题时间,草。(

      给定 (N,K,M),对于每个 (xin[1,N]) 的整数 (x),统计多重集 ({s}) 的个数,使得集合元素的平均数为 (x),且满足对于任意 (i)(s_iin[1,N])(sum_j[s_i=s_j]le K),即相同元素至多出现 (K) 次。答案对 (M) 取模。

      (N,Kle100)

    (mathcal{Solution})

      首先,看到“平均数”“中位数”之类的 AtCoder 比较喜欢的东西,肯定要进行转化。这里可以转化为“偏移量”:假设已钦定平均数 (x),那么一个元素 (v) 的贡献就是 (v-x),最终满足贡献和为 (0) 即可。对于任意多重集 ({s}),记 (i) 在其中出现的次数为 (k_i~(iin[1,N])),那么:

    [egin{aligned} &sum_{i=1}^nik_i=x|{s}|\ Leftrightarrow &sum_{i=1}^n(i-x)k_i=0\ Leftrightarrow &sum_{i=1}^{x-1}(x-i)k_i=sum_{i=x+1}^n(i-x)k_i end{aligned} ]

      写着形象点就是满足:

    [k_{x-1}+2k_{x-2}+cdots+(x-1)k_1=k_{x+1}+2k_{x+2}+cdots+(n-x)k_n ]

      然后非常 amazing 地发现等号左右描述的是同一形式的问题:求 ({k_n}) 的个数,使得 (sum_{i=1}^nik_i=s),且满足 (k_iin[0,K]),其中 (n,s) 是形式参数。这个问题显然可以 DP:令 (f(i,j)) 表示 (sum_{t=1}^itk_t=j) 的方案数,枚举新加入的 (k_{i+1}) 的值即可转移。

      第一维大小显然为 (n),考虑第二维的大小,我们只需要求出能使等式左右两边同时取到的 (j) 值。所以 (jle K(1+2+cdots+lfloorfrac{n}2 floor))

      最后,求答案时,枚举等式左右同时取到的值,乘法原理乘起来,别忘了乘上 (k_x)(K+1) 种取值,在减去空集的 (1)。(真啰嗦 owo。

      总复杂度 (mathcal O(N^3K)) 带小于 (frac{1}8) 的巨小常数(有问题欢迎指出 w),DP 建议刷表,考虑到无用状态数较多,跑得可快啦!

    (mathcal{Code})

    /* Clearink */
    
    #include <cstdio>
    
    inline void wint ( int x ) {
    	if ( 9 < x ) wint ( x / 10 );
    	putchar ( x % 10 ^ '0' );
    }
    
    const int MAXN = 100;
    int N, K, M, f[MAXN + 1][MAXN * ( MAXN / 2 + 1 ) * ( MAXN >> 1 ) / 2 + 5];
    
    inline void addeq ( int& a, const int b ) { ( a += b ) < M ? 0 : a -= M; }
    
    inline void initDP () {
    	f[0][0] = 1;
    	int sbound = K * ( N / 2 + 1 ) * ( N >> 1 ) >> 1;
    	for ( int i = 0; i < N; ++ i ) {
    		for ( int j = 0, cur; j <= sbound; ++ j ) {
    			if ( !( cur = f[i][j] ) ) continue;
    			for ( int k = 0, s = j; k <= K && s <= sbound; ++ k, s += i + 1 ) {
    				addeq ( f[i + 1][s], cur );
    			}
    		}
    	}
    }
    
    inline int solve ( const int x ) {
    	// k[x-1]+2k[x-2]+...+(x-1)k[1] = k[x+1]+2k[x+2]+...+(n-x)k[n].
    	int sbound = x - 1 < N - x ? x - 1 : N - x, ret = 0;
    	sbound = K * sbound * ( sbound + 1 ) >> 1;
    	for ( int s = 0; s <= sbound; ++ s ) {
    		addeq ( ret, 1ll * f[x - 1][s] * f[N - x][s] % M );
    	}
    	return ( ret * ( K + 1ll ) % M + M - 1 ) % M;
    }
    
    int main () {
    	scanf ( "%d %d %d", &N, &K, &M );
    	initDP ();
    	for ( int i = 1; i <= N; ++ i ) {
    		wint ( solve ( i ) );
    		putchar ( '
    ' );
    	}
    	return 0;
    }
    
  • 相关阅读:
    继承项目第13周项目1基类中成员的访问限定符和派生类的继承方式
    架构业务wait和sleep区别狭义jiavaBean规范,三层架构模式
    文件文本编辑器ASP.net使用CKEditor(html文本编辑器)
    彩信对象android(5)_发彩信操作
    方法接口UML统一建模语言,java中七种设计原则,
    jquery实现jQuery实现图片轮播效果,jQuery实现焦点新闻
    设备文件BSP及嵌入式驱动开发笔记
    Invalidate
    C#集合类使用范例
    判断一段程序是由C 编译程序还是由C++编译程序编译的
  • 原文地址:https://www.cnblogs.com/rainybunny/p/13817640.html
Copyright © 2011-2022 走看看