之前訓練賽上碰到了這么一道題:Problem - E - Codeforces
要求 \(\sum_{i=1}^n i^5\ (n~is~so~big)\),一般只會記平方數,立方數前綴和公式。這倆我都經常記不住
好在之前學過如果通過低次的次方數前綴和推到高次,但寫這題的時候忘了,又滾去百度了一波。
為了加深記憶,寫個博客記錄下到底是怎么推的。
話說 \(i^k\) 到底是叫次方數還是叫自然數冪來着?
已知 \(\sum_{i=1}^n i=\dfrac {n*(n+1)}2\),求 \(\sum_{i=1}^n i^2\):
同理,要求立方數的前綴和公式,則對 \(\sum_{i=1}^n (i+1)^4\) 進行同樣的操作,同時我們也可從中得出一個結論:\(n\) 次方數的前綴和公式一定是個 \(n+1\) 次多項式。
這樣推在高次的時候會挺麻煩的,可能要在草稿紙上算挺久化簡化一年,但確實簡單易懂,好想好記。
而且如果時間復雜度允許的話,也不用特意花大量時間去計算式子並化簡、然后套公式 \(O(1)\) 求解;只需在草稿紙上根據二項式定理多展開幾項,然后寫好對應的函數 calx(n)
計算\(\sum_{i=1}^n i^x\),類似遞歸地調用一下即可,時間復雜度也是常數級的。
比如要求 \(i^7\) 次方,那我們把 \(i^{4+1}\sim i^{7+1}\) 都展開一下,然后 cal7(n)
調用 cal6(n)
調用 cal5(n)
。。。一直到 cal3(n)
(一般立方數的前綴和我們知道)遞歸結束。假遞歸,每個函數都不一樣
也許可以寫個函數指針數組?沒研究過,不太清楚,但腦補一下感覺好像可行。
感覺可能還有更好的方法來求解次方數前綴和問題?但私以為這個就夠用了,於是就懶得去找了,總之先放這兒,以后如果學到了啥更優秀的方法再回頭更新一波。估計是莫得了
順便貼一下該題的代碼,二分 + Java 處理大數。訓練時寫的,賊丑,慎看
import java.math.BigInteger;
import java.util.Scanner;
class Main {
static BigInteger cal(BigInteger x){
BigInteger x2=x.multiply(x);
BigInteger x1=x.add(BigInteger.ONE);
x1=x1.multiply(x1);
BigInteger ret=x2.multiply(x1);
// System.out.println(ret);
x=x.multiply(BigInteger.valueOf(2));
x2=x2.add(x2);
ret=ret.multiply(x.add(x2).subtract(BigInteger.ONE));
ret=ret.divide(BigInteger.valueOf(12));
return ret;
}
public static void main(String[] args) {
String P=new String();
String Q=new String();
Scanner in =new Scanner(System.in);
P=in.next();
Q=in.next();
BigInteger p=new BigInteger(P);
BigInteger q=new BigInteger(Q);
// System.out.println(p.divide(q));
BigInteger x=BigInteger.ZERO;
BigInteger ans=BigInteger.ZERO;
BigInteger d=BigInteger.ONE;
for(long i=1;;i++){
BigInteger ti=BigInteger.valueOf(i).pow(5);
BigInteger tx=ti.multiply(q);
if(tx.compareTo(p)>=0){
d=BigInteger.valueOf(i);
ans=tx;
break;
}
x=x.add(tx);
x=x.subtract(p);
// x=x.add(tx);
// x=x.subtract(p);
// if(x.multiply(BigInteger.valueOf(-1)).compareTo(p)>=0||tx.compareTo(p)>=0){
// d=BigInteger.valueOf(i);
// ans=tx;
// break;
// }
}
// System.out.println("x = "+x);
// System.out.println("d = "+ d);
x=p.subtract(x);
System.out.println(x);
// System.out.println(d);
// System.out.println(cal(d));
BigInteger dt=cal(d);
BigInteger mb=new BigInteger("1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
mb=mb.subtract(ans);
BigInteger l=d.add(BigInteger.ONE);
BigInteger r=new BigInteger("100000000000000000000");
BigInteger y=l;
while(l.compareTo(r)<=0){
BigInteger mid=l.add(r).divide(BigInteger.valueOf(2));
// System.out.println("mid = "+mid);
BigInteger t=cal(mid).subtract(dt);
t=t.multiply(q);
BigInteger cj=mid.subtract(d);
t=t.subtract(cj.multiply(p));
if(t.compareTo(mb)>=0){
y=mid;
r=mid.subtract(BigInteger.ONE);
}
else {
l=mid.add(BigInteger.ONE);
}
}
System.out.println(y);
}
}
剛說不會學就學到新東西了好快のflag回收,用拉格朗日插值可以更加優美地解決此類問題,請移步:拉格朗日插值學習筆記 - olderciyuan - 博客園 (cnblogs.com)。
