HDU 6467 简单数学题 (组合数学推导)

题意

题解

\begin{align*} F(n) [Step\;1] &= \sum_{j=1}^{n}\sum_{i=0}^{j}C_j^i\times i \\ [Step\;2] &= \sum_{j=1}^{n}\frac{\sum_{i=0}^{j}C_j^i\times i+\sum_{i=0}^{j}C_j^{j-i}\times i}{2}\\ [Step\;3] &= \sum_{j=1}^{n}\frac{\sum_{i=0}^{j}C_j^i\times i+\sum_{i=0}^{j}C_j^i\times (j-i)}{2}\\ [Step\;4] &= \sum_{j=1}^{n}\frac{j\times \sum_{i=0}^{j}C_j^i}{2}\\ [Step\;5] &= \sum_{j=1}^{n}\frac{j\times 2^j}{2} = \sum_{j=1}^{n}j\times 2^{j-1} \\ [Step\;6] &= (2^n-1)\times (n+1)-\sum_{j=1}^{n}(n+1-j)\times 2^{j-1} \\ [Step\;7] &= (2^n-1)\times (n+1)-\sum_{j=1}^{n}(2^j-1)\\ [Step\;8] &= (2^n-1)\times (n+1)-(\sum_{j=1}^{n}2^j-n)\\ [Step\;9] &= (2^n-1)\times (n+1)-(2^{n+1}-2-n)\\ [Step\;10] &= (2^{n \% \phi(mod)}-1)\times (n+1)-(2^{(n+1)\% \phi(mod)}-2-n) \end{align*}

Step 1

把原式进行了最基本的变换,把 i 移到右边,并先枚举 j ,这里 i 从 0 开始枚举,并不影响答案,因为 C(j,0) 乘 0 后没有影响,但是这样方便后面的推导

Step 2

因为

C_n^m=C_n^{n-m}

这是最基本的组合数性质,所以把右边一坨加上自己的变式,再除以2

Step 3

把右上角换元,用 j - i 替换 i 

Step 4

换元后,发现两坨可以合并,把同类项的系数加起来恰好等于 j 

Step 5

因为

\sum_{i=0}^{n}C_n^i=2^n

这也是组合数的性质之一,用组合意义可以解释为“从n个球中依次选0,1,2,...个的方案数之和就相当于每个数可以选与不选,也就是2^n”

所以就可以少枚举一层了。

我看这个式子后,想出了矩阵加速的解法

一个3*1的向量矩阵乘3*3的转移矩阵,向量里依次维护2^(j-1)、j * 2^(j-1)、sum。

转移矩阵也很好推,码码码……

……(Time Limit Exceeded)……

事后我算了一下复杂度,最大为O(300000*64*27)=O(518400000),好像过不了,只好继续推式子

Step 6

把 ×j 换成 ×[ (n + 1) - (n + 1 - j) ],然后分开,右边就相当于这样一个数

\begin{matrix} n &\times (1)_2 \\ +(n-1) &\times (10)_2 \\ +(n-2)&\times (100)_2\\ +(n-3)&\times (1000)_2\\ +...... \end{matrix}

Step 7

(这一步可能难懂,请读者感性理解)

把Step 6里右边那个数拆开,

先设该数为 n 个数相加,n 个数初始为零

把 n×(1) 拆成 n 个 (1) 相加,然后依次加到 n 个数中

再把 (n-1)×(10) 拆成 n-1 个 (10) 相加,然后依次加到后 n-1 个数中

再把 (n-2)×(100) 拆成 n-2 个 (100) 相加,然后依次加到后 n-2 个数中

……

最后再把这 n 个数相加,发现

\begin{matrix} n &\times (1)_2 \\ +(n-1) &\times (10)_2 \\ +(n-2)&\times (100)_2\\ +(n-3)&\times (1000)_2\\ +...... \end{matrix}\;\;=\;\; \begin{matrix} &(1)_2\\ +&(11)_2\\ +&(111)_2\\ +&(1111)_2\\ +&...... \end{matrix}

Step 8

把 2^j-1 的 1 提出来,直接在右边+n,顿时变得清爽

Step 9

\sum_{j=1}^{n}2^j=(1111...110)_2=(10000...000)_2-(10)_2=2^{n+1}-2

这步是把它二进制展开,左边有n个连续的1,右边一个0,可以通过更高一位的 1 减去 (10) 得到

大功告成,可以直接用快速幂了!

Step 10

这步其实没什么必要,只是笔者想到可以(凑个整数)用欧拉定理优化,于是就用了,最大数据可以把常数除以 2

CODE

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
#define MAXN 505
#define MAXM 35
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x)&(x))
//#define int LL
using namespace std;
inline LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s == '-')f = -1;s = getchar();}
	while(s >= '0' && s <= '9') {x = x * 10 + (s - '0');s = getchar();}
	return x * f;
}
const int jzm = 1000000007;
int n,m,i,j,s,o,k;
int qkpow(int a,LL b,int zxy) {
	int res = 1;
	while(b > 0) {
		if(b & 1) res = res *1ll* a % zxy;
		a = a *1ll* a % zxy;
		b >>= 1;
	}
	return res % zxy;
}
int main() {
	LL N;
	while(scanf("%lld",&N) == 1) {
		int ans = (qkpow(2,N % (jzm-1),jzm) + (jzm-1)) % jzm *1ll* (N%jzm + 1ll) % jzm;
		ans = (ans +0ll+jzm - qkpow(2,(N+1) % (jzm-1),jzm)) % jzm;
		(ans += (N+2ll) % jzm) %= jzm;
		printf("%d\n",ans);
	}
	return 0;
}

 

posted @ 2020-09-06 16:55  DD_XYX  阅读(90)  评论(0)    收藏  举报