BZOJ 3157&3516&4126 國王奇遇記

[TOC]php

###BZOJ 3157&3516&4126 國王奇遇記 題目傳送門1 題目傳送門2 題目傳送門3c++

###題意 計算$\sum_{i=1}^{n}i^mm^i$   $%$   $10^9+7$ $(1 \leq n \leq 10^9)$ BZOJ3157:$1 \leq m \leq 200$ BZOJ3516:$1 \leq m \leq 1000$ BZOJ4126:$1 \leq m \leq 50000$ ###題解 通常題目短的都很毒這道題各類作法都有,想不想的出來彷佛只有看推式子的能力了。推不出式子的話那就只能亂搞了。先講一種本身單獨想出來的可是複雜度爆炸的作法,複雜度彷佛是$O(m^3log(n))$的,然而彷佛能夠過。。首先咱們把這個式子用拆成這樣:$\sum_{i=1}^{n}i^mm^i=m*(1^m+m(2^m+m(...m(n^m))))$,而後能夠發現這個式子是很是有規律的,因而咱們就考慮矩乘,但這個$i^m$很是的難弄,因此咱們在矩陣中同時記錄這個$i^m$,而後一塊兒轉移就好了,然而這個看起來很是好寫的作法複雜度很高,然而代碼也調了很久(感受本身蒿菜啊~),因此代碼就不放了2333。git

而後開始講一下比較高端的作法。。首先是$O(m^2log(n))$的作法,咱們記$f(n,k)=\sum_{i=1}^{n}i^km^i$,那麼: $$ \begin{array} & f(n+1,k) &= \sum_{i=1}^{n+1}i^km^i \ &=\sum_{i=2}^{n+1}i^km^i+m \ &=\sum_{i=1}^{n}(i+1)^km^{i+1}+m \ &=\sum_{i=1}^{n}m^{i+1}\sum_{j=0}^kC_k^ji^j+m \ &=\sum_{j=0}^{k}C_k^jm\sum_{i=1}^ni^jm^i+m \ &=\sum_{j=0}^kC_k^jmf(n,j)+m \end{array} $$ 這樣咱們就能夠用$O(m^2)$的時間從$n$轉移到$n+1$。咱們繼續: $$ \begin{array} & f(2n,k) &= \sum_{i=1}^{2n}i^km^i \ &= \sum_{i=1}^{n}i^km^i+\sum_{i=n+1}^{2n}i^km^i \ &= f(n,k)+\sum_{i=1}^{n}(i+n)^km^{i+n} \ &= f(n,k)+\sum_{i=1}^{n}m^{i+n}\sum_{j=0}^{k}C_k^ji^jn^{k-j} \ &= f(n,k)+\sum_{j=0}^{k}C_k^jn^{k-j}m^n\sum_{i=1}^{n}i^jm^i \ &= f(n,k)+m^n\sum_{j=0}^{k}C_k^j*n^{k-j}*f(n,j) \end{array} $$ 這樣咱們就能夠一樣用$O(m^2)$的時間從$n$轉移到$2n$。接下來就是比較簡單的遞歸處理了,總複雜度爲$O(m^2log(n))$。 ###Code Of BZOJ3157算法

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
#define PAUSE printf("Press Enter key to continue..."); fgetc(stdin);
const int Md=1e9+7;
const int N=205;
ll n;
int m;
ll C[N][N],f[N][N];
/*==================Define Area================*/
ll Powe(ll x,ll y) {
	ll res=1;
	while(y) { 
		if(y&1) res=1ll*res*x%Md;
		x=1ll*x*x%Md;
		y>>=1;
	}
	return res;
}

void Init() {
	C[0][0]=1;
	for(int i=1;i<=m;i++) {
		C[i][0]=1;
		for(int j=1;j<=m;j++) {
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%Md;
		}
	}
}

void Calc(ll x,int y) {
	if(x==1) {
		for(int i=0;i<=m;i++) {
			f[y][i]=m;
		}
		return ;
	}
	if(x&1) {
		Calc(x-1,y+1);
		for(int i=0;i<=m;i++) {
			for(int j=0;j<=i;j++) {
				(f[y][i]+=(f[y+1][j]*C[i][j])%Md)%=Md;
			}
			(f[y][i]*=m)%=Md;(f[y][i]+=m)%=Md;
		}
	}
	else {
		Calc(x/=2,y+1);ll Pw=Powe(m,x);
		for(int i=0;i<=m;i++) {
			for(int j=0;j<=i;j++) {
				(f[y][i]+=(C[i][j]*Powe(x,i-j)%Md*f[y+1][j])%Md)%=Md;
			}
			(f[y][i]*=Pw)%=Md;
			(f[y][i]+=f[y+1][i])%=Md;
		}
	}
}

int main() {
	read(n);read(m);
	Init();
	Calc(n,1);
	printf("%lld\n",f[1][m]);
	return 0;
}

然而這麼高端的作法過不去BZOJ3516,畢竟老年機因而咱們思考更加優越的作法,咱們發現上面一個作法實際上能夠用一維記錄,由於$n$只能轉移到$n+1$或者$2n$,這樣咱們就須要思考是否能夠省去$n$這一維。因而咱們記$f(k)=\sum_{i=1}^ni^k+m^i$ 那麼: $$ \begin{array} & (m-1)f(k) &= (m-1)\sum_{i=1}^{n}i^km^i \ &= \sum_{i=1}^ni^km^{i+1} -\sum_{i=1}^ni^km^i \ &= \sum_{i=2}^{n+1}(i-1)^km^i-\sum_{i=1}^ni^km^i \ &= \sum_{i=1}^{n}((i-1)^k-i^k)m^i+n^km^{n+1} \ &= \sum_{i=1}^{n}m^i\sum_{j=0}^{k-1}C_k^j(-1)^{k-j}i^j+n^km^{n+1} \ &= \sum_{j=0}^{k-1}C_k^j(-1)^{k-j}\sum_{i=1}^{n}i^jm^i+n^km^{n+1} \ &= \sum_{j=0}^{k-1}C_k^j(-1)^{k-j}f(j)+n^km^{n+1} \end{array} $$ 因而咱們能夠得出$f(k)=\frac{\sum_{j=0}^{k-1}C_k^j(-1)^{k-j}*f(j)+n^km^{n+1}}{m-1}$。這樣咱們就獲得了$O(m^2)$的作法,就能夠過BZOJ3516的數據範圍了。spa

###Code Of BZOJ 3516code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
#define PAUSE printf("Press Enter key to continue..."); fgetc(stdin);
const ll Md=1e9+7;
const int N=2005;
int n,m;
ll C[N][N],f[N];
/*==================Define Area================*/
void Init() {
	C[0][0]=1;
	for(int i=1;i<=m;i++) {
		C[i][1]=i;C[i][0]=1;
		for(int j=2;j<=i;j++) {
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%Md;
		}
	}
}

ll Powe(ll x,int y) {
	ll res=1;
	while(y) {
		if(y&1) res*=x,res%=Md;
		x*=x;x%=Md;
		y>>=1;
	}
	return res;
}

int main() {
	read(n);read(m);
	Init();
	if(m==1) return printf("%lld\n",((ll)(n+1)*n/2)%Md),0;
	ll Sum=Powe(m,n+1);
	f[0]=(Sum-m+Md)%Md;
	ll Div=Powe(m-1,Md-2);f[0]*=Div;f[0]%=Md;
	for(int i=1;i<=m;i++) {
		Sum*=n;Sum%=Md;f[i]=Sum;
		for(int j=0;j<i;j++) {
			int F=((i-j)&1)?-1:1;
			(f[i]+=C[i][j]*F*f[j]%Md)%=Md;
		}
		(f[i]+=Md)%=Md;
		f[i]*=Div;f[i]%=Md;
	}
	printf("%lld\n",f[m]);
	return 0;
}

而後。。$O(m)$的高端算法。。留個坑,奶一口確定可以填回來。遞歸

相關文章
相關標籤/搜索