lahlahblog喵~

浅谈小多项式快速幂

lahlah·2021-05-27 09:10·147 次阅读

浅谈小多项式快速幂

由模拟赛出了一个,完全不会
且GDKOI2021也出过,当时也是完全不会
可得半年来没有一点进步
Q . E . D Q.E.D Q.E.D
因为不会小多项快速幂被yyc D爆了
你怎么这么**菜!
故有了这篇文章


假设当前多项式为 F ( x ) F(x) F(x)
F ( x ) k + 1 = F ( x ) k F ( x ) F(x)^{k+1}=F(x)^{k}F(x) F(x)k+1=F(x)kF(x)
分别求导
左边用复合函数求导,右边用乘法法则
( k + 1 ) F ( x ) k F ′ ( x ) = ( F ( x ) k ) ′ F ( x ) + F ( x ) k F ′ ( x ) (k+1)F(x)^kF'(x)=(F(x)^{k})'F(x)+F(x)^kF'(x) (k+1)F(x)kF(x)=(F(x)k)F(x)+F(x)kF(x)
k F ( x ) k F ′ ( x ) = ( F ( x ) k ) ′ F ( x ) \large kF(x)^kF'(x)=(F(x)^{k})'F(x) kF(x)kF(x)=(F(x)k)F(x)
记住上面这条式子,后面可以推
k ∑ F ′ [ i ] F k [ n − i ] = ∑ F [ i ] ( n − i + 1 ) F k [ n − i + 1 ] k\sum F'[i]F^k[n-i]=\sum F[i](n-i+1)F^k[n-i+1] kF[i]Fk[ni]=F[i](ni+1)Fk[ni+1]
把右边那块 i = 0 i=0 i=0的分离出来,移项
F [ 0 ] ( n + 1 ) F k [ n + 1 ] = k ∑ F ′ [ i ] F k [ n − i ] − ∑ i = 1 F [ i ] ( n − i + 1 ) F k [ n − i + 1 ] F[0](n+1)F^k[n+1]=k\sum F'[i]F^k[n-i]-\sum\limits_{i=1} F[i](n-i+1)F^k[n-i+1] F[0](n+1)Fk[n+1]=kF[i]Fk[ni]i=1F[i](ni+1)Fk[ni+1]
这样就得到了一个关于 F k F^k Fk的递推式
复杂度与 F F F的项数有关
假设多项式的非0系数有m个,要求0…n项
时间复杂度就是 O ( n m ) O(nm) O(nm)


来道题康康
GDKOI Day 3 T2 ??? 好像是

B 二叉树

题目大意#

有个 n n n个叶子节点的二叉树,每个叶子节点的值为1,每个非叶子节点均有2个儿子。
k k k条信息,对于每个非叶子节点,如果其左儿子的叶子节点个数为 s i s_i si,则其美观程度为 v i v_i vi.
如果均不满足,则美观程度为 A A A
二叉树的美观程度为所有节点美观程度的乘积,求所有可能二叉树的美观程度之和
答案对 1000000007 1000000007 1000000007取模

题解#

f [ n ] f[n] f[n]表示叶子节点个数为 n n n的答案,容易得出
f [ 1 ] = 1 f[1]=1 f[1]=1
f [ n ] = ∑ k = 1 n − 1 c [ k ] f [ k ] f [ n − k ] \large f[n]=\sum\limits_{k=1}^{n-1}c[k]f[k]f[n-k] f[n]=k=1n1c[k]f[k]f[nk]
其中 c [ s i ] = v i c[s_i]=v_i c[si]=vi,其他为 A A A
然而观察上面那个式子,发现 c [ k ] c[k] c[k]是点乘上去的,不太方便写成卷积的形式,考虑把那些项挖掉
就是设 g [ k ] = ( c [ k ] − A ) ∗ f [ k ] g[k]=(c[k]-A)*f[k] g[k]=(c[k]A)f[k]
上面那个式子就可以写成
f [ n ] = ∑ k = 1 n − 1 f [ k ] f [ n − k ] + ∑ k = 1 n g [ k ] f [ n − k ] \large f[n]=\sum\limits_{k=1}^{n-1}f[k]f[n-k]+\sum\limits_{k=1}^{n}g[k]f[n-k] f[n]=k=1n1f[k]f[nk]+k=1ng[k]f[nk]
F ( x ) F(x) F(x) f n {f_n} fn的多项式, G ( x ) 为 g 的 G(x)为g的 G(x)g
可得
F ( x ) = A F 2 ( x ) + G ( x ) F ( x ) + x \large F(x)=AF^2(x)+G(x)F(x)+x F(x)=AF2(x)+G(x)F(x)+x
最后那个 + x +x +x表示加上只有一个点的方案数,准确来讲是 c [ 1 ] x c[1]x c[1]x,不过无所谓了
把每个 c [ i ] / A c[i]/A c[i]/A变成
F ( x ) = F 2 ( x ) + G ( x ) F ( x ) + x \large F(x)=F^2(x)+G(x)F(x)+x F(x)=F2(x)+G(x)F(x)+x
解方程可得
F ( x ) = 1 − G ( x ) − ( 1 − G ( x ) ) 2 − 4 x 2 \large F(x)=\frac{1-G(x)-\sqrt{(1-G(x))^2-4x}}{2} F(x)=21G(x)(1G(x))24x
然后直接套小多项式快速幂
开根就令 k = 1 2 k=\frac{1}{2} k=21
注意如果每次跟新 g g g后重新开根,时间复杂度不太对
可以带回上面解方程的那条式子求出根号里的那坨东西
code:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pi;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
typedef vector<pi> poly;
const int mod = 1000000007; 
int add(int x, int y) { x += y;
	if(x >= mod) x -= mod;
	return x;
}
int sub(int x, int y) { x -= y;
	if(x < 0) x += mod;
	return x;	
}
inline int mul(int x, int y) {
	return 1ll * x * y % mod;
}
int qpow(int x, int y) {
	int ret = 1;
	for(; y; y >>= 1, x = mul(x, x)) if(y & 1) ret = mul(ret, x);
	return ret;
}
const int N = 1000500;
int n, k, A, inv[N];
void init() {
	inv[1] = 1;
	for(int i = 2; i <= n; i ++)
		inv[i] = mul(inv[mod % i], (mod - mod / i));
}
poly deriv(poly a) {
	poly b;
	for(int i = 0; i < a.size(); i ++) {
		pi x = a[i];
		if(x.fi) b.pb(mp(x.fi - 1, mul(x.se, x.fi)));
	}
	return b;
}
poly pf (poly a) {
	poly c;
	for(int i = 0; i < a.size(); i ++) {
		pi x = a[i];
		c.pb(mp(x.fi * 2, mul(x.se, x.se)));
		for(int j = i + 1; j < a.size(); j ++) {
			pi y = a[j];
			c.pb(mp(x.fi + y.fi, mul(2 * x.se, y.se)));	
		}
	}
	return c;
}
poly getg2(poly g) {
	g.pb(mp(0, mod - 1)); g = pf(g);
	g.pb(mp(1, mul(mod - 4, 1)));
	
	return g;
}
int sq[N], f[N];
poly Fd;
void calc(int n, poly F) {
	int ret = 0;
	for(int i = 0; i < Fd.size(); i ++) {
		pi x = Fd[i];
		if(0 <= x.fi && x.fi <= n) {
			ret = add(ret, mul(x.se, sq[n - x.fi]));
		}	
	}
	ret = mul(ret, inv[2]);
	for(int i = 0; i < F.size(); i ++) {
		pi x = F[i];
		if(1 <= x.fi && x.fi <= n) {
			ret = sub(ret, mul(n - x.fi + 1, mul(x.se, sq[n - x.fi + 1])));
		}
	}
	sq[n + 1] = mul(ret, inv[n + 1]);
	f[n + 1] = mul(sq[n + 1], mod - inv[2]);
}
poly g, g2;
pi a[N];
int main(){
	scanf("%d%d%d", &n, &k, &A); init();
	for(int i = 1; i <= k; i ++) scanf("%d%d", &a[i].fi, &a[i].se), a[i].se = mul(a[i].se, qpow(A, mod - 2));
	sort(a + 1, a + 1 + k);
	g2 = getg2(g);
	sq[0] = 1, sq[1] = mod - 2, f[1] = 1; 
	int j = 1;
	for(int i = 1; i <= k; i ++) {
		Fd = deriv(g2);
		for(; j < a[i].fi; j ++) calc(j, g2);
		g.pb(mp(a[i].fi, mul(f[a[i].fi], sub(a[i].se, 1))));
		g2 = getg2(g);
	
		for(int ii = 0; ii <= a[i].fi; ii ++) sq[ii] = mul(1, mul(f[ii], mod - 2));
		for(int ii = 0; ii < g.size(); ii ++) {
			pi x = g[ii];
			sq[x.fi] = sub(sq[x.fi], x.se);
		}
		sq[0] = add(sq[0], 1);
	}
	Fd = deriv(g2);
	for(; j < n; j ++) calc(j, g2);
	printf("%d", mul(f[n], qpow(A, n - 1)));
	return 0;
}

好像很NB的亚子
还是要多练习

posted @   lahlah  阅读(147)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
目录