恰好是去年的这个时候左右,我做了这个系列的前两题。。(其实相当于只做了一题hhh)然而当时的姿势水平非常低,式子大概都是瞎jb凑出来的。。。(也有可能看了波题解?)反正到了第三题就彻底一脸懵逼了。。记得看题解也看不懂是个毛。。后来就弃了。。
一年之后的现在。。。前几天dwj老司机误以为我做过这个题(的再加强版)。。于是就来问我怎么证答案是用一个多项式来表示的。。。我想我好像都看(翻)完了具体数学求和的那几章。。不如就来肛一波这个题啊。。。于是总共花了差不多三天,总时长大概5h+。。期间把我学过的不少组合数学的东西都搞过了。。独立艹掉了这个题真是感动QAQ。。。然而感觉现在的姿势水平还是好低啊。。(最后还是有一点同学的帮助hhhhh)
进入正题啦。
给定一个与
n
无关的
比较套路的搞法是,因为 km 容易被表示,所以尝试对这一项展开。于是稍微改变一下求和指标得到
可以看到后面那坨东西和一开始要算的东西形式类似,于是记
注意将 fj 带进去时要加上 k==0 时的项,看起来像是0,但是实际上当 j==0 时就会出问题。我们定义 00=1 ,然后再加上这个 1 就对了。于是变成
稍微移一下项可得
至此,我们得到了一个 O(m2) 递推的方法。
接下来就不是那么自然了,但也是可以想到的。
注意到
ft
的递推式里有一个
mn∗nt
,这个非常重要,应该意识到,如果将
ft
完全展开,可能会有一个
mn∗Pt(n)
,其中
Pt(n)
是一个关于
n
的多项式。
因此我们先假设
则
对比系数得
注意到
Pt(n)
的常数项形式和
Qt(m)
的形式相同,并且恰好
Qt(m)
与
n
无关,而显然
其中 Fm(n) 是一个 m 次多项式。
然而知道这个有什么卵用呢???
如果我们将多项式换个表示方法,就会有神效。对于多项式
(实际上牛顿级数只用求和到 m ,但是这里为了方便就求和到
这实际上是 cn=ΔnFm(0) ,所以顺带解释了为什么牛顿级数只求和到 m (对于
我们再将
这意味着,知道 Fm(0)...Fm(m) 的值就可以算出来任意一个 Fm(n) 的值啦
(解锁成就:独立推出 O(m) 求 Fm(n) )
UPD:原来这玩意就是传说中的“ 线性插值”啊。。。23333333
接下来就是算 Fm(0)...Fm(m) 的问题了。
我们来重新定义一下,设答案为 S(n)=∑n−1k=1kmmk ,那么有
对 S(n) 取一次差分,可以得到 Fm(n) 到 Fm(n+1) 的关系:
也就是
这样可以递推出 Fm(1)...Fm(m) 的值。但是我们好像根本不知道 Fm(0) 啊???
其实这一步就简单的了。。。考虑对 S(n) 构造一个比较奇怪的 m+1 阶差分:
这个时候前面一半就是对 Fm(n) 取了个 m+1 差分,就消掉了。所以再稍微变一下,代 n=0 就得到了
S(0)...S(m) 显然是可以直接算出来的。。。于是我们就可以算出来 Fm(0)...Fm(m) 啦。。。
接着利用前面的那个求值方法就可以算出 Fm(n+1) ,然后就可以算出来 S(n+1) 了。。。
这样就结束了。。。?
我们来看一下复杂度。。。全程看起来指标都是
O(m)
的很吼。。。
。。。吗?
仔细看看会发现算
S(m+1)=∑mk=1kmmk
的时候有
m
个
不管啦直接肛
结果第一次交大常数版本的时候TLE了
瞎改了点东西这破
O(mlogm)
就跑到7s+了
然后经同学提醒。。。这玩意是积性的啊。。。不是可以直接筛么???
[捂脸熊.jpg]
于是就轻易地
O(m)
啦。
完结撒花!
【一些东西】
不太会写脚注就在这里把中间用过的一些《具体数学》[人民邮电出版社,第二版]上的结论一次性列出来吧。
n
阶差分:P156,(5.40)
牛顿级数:P157,没有标成公式,毕竟只是多项式的一种表示形式吧。
二项式反演:用了P160,(5.48)的另一种形式
(nk)(kj)=(nj)(n−jk−j) :P138,(5.21)。表5-4称其为”三项式版恒等式”。
∑k≤m(nk)(−1)k=(−1)m(n−1m) :P136,(5.16)。带交错符号的二项式系数和。
初始的式子还可以用分部求和搞出来,P234,(6.69)。
【主要代码】
int n , m;
arr fact , invF , pw;
arr F , c2 , pwk;
void input() {
n = rd() , m = rd();
}
inline void init() {
m ++;
fact[0] = invF[0] = 1;
rep (i , 1 , m) fact[i] = mul(fact[i - 1] , i);
invF[m] = Pow(fact[m] , mod - 2);
per (i , m , 1) invF[i - 1] = mul(invF[i] , i);
pw[0] = 1;
m --;
rep (i , 1 , m + 1) pw[i] = mul(pw[i - 1] , m);
static arr vis , pr;
int tot = 0;
pwk[1] = 1;
rep (i , 2 , m + 1) {
if (!vis[i])
pr[++ tot] = i , pwk[i] = Pow(i , m);
rep (j , 1 , tot) if (i * pr[j] > m + 1) break; else {
vis[i * pr[j]] = 1;
pwk[i * pr[j]] = mul(pwk[i] , pwk[pr[j]]);
if (i % pr[j] == 0) break;
}
}
}
inline int _C(int n , int m) {
return n >= m ? mul(fact[n] , mul(invF[m] , invF[n - m])) : 0;
}
inline void get_F0() {
int invm = Pow(m , mod - 2) , mm = 1;
int up = 0 , down = 0;
int S = 0;
rep (k , 0 , m + 1) {
int tmp = _C(m + 1 , k);
if ((m + 1 - k) & 1) tmp = mod - tmp;
tmp = mul(tmp , mm);
up = add(up , mul(tmp , S));
down = add(down , tmp);
mm = mul(mm , invm);
S = add(S , mul(pw[k] , pwk[k]));
}
up = mod - up;
F[0] = mul(up , Pow(down , mod - 2));
}
inline void calc_F() {
int invm = Pow(m , mod - 2);
rep (k , 1 , m + 1) {
F[k] = add(pwk[k - 1] , F[k - 1]);
F[k] = mul(F[k] , invm);
}
}
inline int binom(int n , int m) {
int t = invF[m];
For (i , 0 , m)
t = mul(t , n - i);
return t;
}
inline int inv(int i) {
return mul(fact[i - 1] , invF[i]);
}
inline void calc_ans() {
int ans = 0;
int c1 = 1;
c2[m] = 1;
Dwn (j , m , 0) c2[j] = mul(c2[j + 1] , mul(n - j - 1 , inv(m - j)));
rep (j , 0 , m) {
if (j)
c1 = mul(c1 , mul(n - j + 1 , inv(j)));
int tmp = mul(c1 , F[j]);
tmp = mul(tmp , c2[j]);
if ((m - j) & 1) tmp = mod - tmp;
ans = add(ans , tmp);
}
ans = mul(Pow(m , n) , ans);
ans = dec(ans , F[0]);
printf("%d\n" , ans);
}
void solve() {
if (m == 1) {
int ans = 1ll * n * (n + 1) / 2 % mod;
printf("%d\n" , ans);
return;
}
if (n <= m) {
int ans = 0;
int t = m;
rep (i , 1 , n)
ans = add(ans , mul(t , Pow(i , m))) , t = mul(t , m);
printf("%d\n" , ans);
return;
}
n ++;
init();
get_F0();
calc_F();
calc_ans();
}