poj 1845 Sumdiv 數論--等比數列和(逆元或者遞迴)
先說題意:輸入a和b,求a^b的所有因子之和。
題解:先分解a的質因子,a=p1^t1*p2^t2*...*pk^tk(pi為質數)。再a^b=p1^(t1*b)*p2^(t2*b)*...pk^(tk*b)。選出所有的因子就是列舉所有的ti*b,求和可知sum=(1+p1+...p1^(t1*b))*(1+p2+...p2^(t2*b))*...*(1+pk+...+pk*(tk*b));
而求1+pi+pi^2+...pi^ci(ci=ti*b)有兩種方法:
1.直接用二分遞迴求:舉例來說,1+a+a^2+a^3+a^4=(1+a)*(1+a^2)+a^2;(1+a)=1*(1+a)。根據奇偶二分下去。只要只有一個數為止。
2.是寫出通項公式求解:這是個等比數列,所以由等比公式可得1+pi+...+pi^ci=(pi^(ci+1)-1)/(pi-1)。接著就是快速冪和分數形式的取模了。分數形式的取模也有兩種方法:
(1)逆元:A/B=A*B^(-1),B^(-1)就是B的逆元;B*C%mod=1,則說C是B的逆元,求的方法有拓展歐幾里德公式來求和定理求解。不題mod=9901是質數,所以可以直接定理求解(費馬小定理:a^(mod-1)%mod=1,mod為素數),也可以用尤拉定理,也一樣。。因而(A/B)%mod=A*B^(mod-2);
(2)變換模值:(A/B)%mod=(A%(mod*B))/B%mod。對B*mod取餘,剩餘的值必定是B的倍數,這種方法是用於mod和B小的時候,這題就剛剛好。
注意點:
(1)0^0=1,這題中0的無論多少次都是1.。。,不知是沒有這類的資料還是原本就定義成這樣。
(2)用通項公式求的時候需要注意的是A/B中,如果B是mod的倍數就不能用逆元。。why?因為逆元要求B*B^(-1)%mod=1,但B%mod=0恆成立,找不出模mod下的逆元。
逆元求分數取模程式碼:
耗時:16MS
#include <cstdio> #include <cmath> #include <iostream> #include <algorithm> using namespace std; typedef __int64 LL; const LL mod=9901; LL mul(LL a,LL b,LL n)//大數乘法,直接相乘會爆int64,需要逐位相乘 { LL s=0; while(b) { if(b&1) s=(s+a)%n; a=(a*2)%n; b=b>>1; } return s; } LL pow_mod(LL a,LL b,LL n)//修改後的求次方,避免了爆int64 { a=a%n; LL s=1; while(b) { if(b&1) { s=mul(s,a,n); } a=mul(a,a,n); b=b>>1; } return s; } int main() { LL a,b; while(cin>>a>>b) { if(a<=1||b==0){cout<<1<<endl;continue;} LL ans=1,i,j,k,t,n,m; n=(LL)sqrt(a+0.5); for(i=2;i<=n;i++) { if(a%i==0) { //if((i-1)%mod==0)cout<<"*"<<i<<endl; //cout<<"*"<<endl; t=0; while(a%i==0){ a=a/i; t++; } if((i-1)%mod==0)ans=ans*(pow_mod(i,t*b+1,mod*(i-1))/(i-1))%mod; else ans=ans*(pow_mod(i,t*b+1,mod)-1)*pow_mod(i-1,mod-2,mod)%mod; } } if(a>1) { //if((a-1)%mod==0)cout<<"*"<<a<<endl; if((a-1)%mod==0)ans=ans*(pow_mod(a,b+1,mod*(a-1))/(a-1))%mod; else ans=ans*(pow_mod(a,b+1,mod)-1)*pow_mod(a-1,mod-2,mod)%mod; } cout<<(ans+mod)%mod<<endl; } return 0; }
遞迴二分程式碼:
耗時:32MS
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
const int mod=9901;
int pow_mod(int a,int b)
{
a=a%mod;
int s=1;
while(b)
{
if(b&1)
s=(s*a)%mod;
a=(a*a)%mod;
b=b>>1;
}
return s;
}
int sum(int a,int b)//求1+a+a^2+...+a^b
{
if(b==1)return 1;
if(b&1)return (sum(a,b/2)*(1+pow_mod(a,b/2+1))+pow_mod(a,b/2))%mod;
else return sum(a,b/2)*(1+pow_mod(a,b/2))%mod;
}
int main()
{
int a,b;
while(cin>>a>>b)
{
if(a<=1||b==0){cout<<1<<endl;continue;}
int ans=1,i,j,k,t,n,m;
n=(int)sqrt(a+0.5);
for(i=2;i<=n;i++)
{
if(a%i==0)
{
t=0;
while(a%i==0){
a=a/i;
t++;
}
ans=ans*sum(i,t*b+1)%mod;
}
}
if(a>1)
ans=ans*sum(a,b+1)%mod;
cout<<(ans+mod)%mod<<endl;
}
return 0;
}
相關推薦
poj 1845 Sumdiv 數論--等比數列和(逆元或者遞迴)
先說題意:輸入a和b,求a^b的所有因子之和。 題解:先分解a的質因子,a=p1^t1*p2^t2*...*pk^tk(pi為質數)。再a^b=p1^(t1*b)*p2^(t2*b)*...pk^(tk*b)。選出所有的因子就是列舉所有的ti*b,求和可知sum=(1+p1
POJ——1845 Sumdiv (尤拉篩+快速冪+遞迴二分)
Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine S modulo 9901 (the rest of the division of S by 9901)
POJ-1845-Sumdiv(質因數分解+分治求等比數列和)
首先,對 b=1 的情況進行考慮:A 的約數,可以看成對其質因數分解(假設為m)後,從中取 n 個質因數(每個質因數取的個數不能超過A中有的)相乘。於是這道題我們也是一樣的做法。 的約數就是從A的m個質因數裡,取n個(其中,每個質因數取的個數,不超過 A中有的和B的乘積)
總結——數論:乘法逆元
元素 pre ext pos col 存在 gpo gcd oid 零 乘法逆元 對於縮系中的元素,每個數a均有唯一的與之對應的乘法逆元x,使得 ax≡1(mod n) 。 一個數有逆元的充分必要條件是 gcd(a,n)=1 ,此時逆元唯一存在。 逆元的含義:模 n
『數論』乘法逆元
在求解除法取模問題\((a \div b) \mod m\)時,我們可以轉化為\([a \mod (b \times m)]\div b\) 但是如果\(b\)很大,則會出現爆精度問題,所以我們避免使用除法直接計算。 可以使用逆元將除法轉換為乘法:假設\(b\)存在乘法逆元,即與\(m\)互質(充要條件)
數論文章----關於逆元的求法(尤拉定理,階乘逆元,費馬小定理,模質數p的情況)
乘法逆元 對於縮系中的元素,每個數a均有唯一的與之對應的乘法逆元x,使得ax≡1(mod n) 一個數有逆元的充分必要條件是gcd(a,n)=1,此時逆元唯一存在 逆元的含義:模n意義下,1個數a如果有逆元x,那麼除以a相當於乘以x。 下面給出求逆元的幾種方法: 1
【數論】 通過逆元實現大整數除法的取餘
當題目中資料較大,而且計算中出現過除法的時候。往往取模會出錯 當計算 (A/B) % c 等價於 (A*B1)% c 其中 B1 是 B 的逆元。 那麼逆元如何求呢。 先給出逆元的定
【數論】乘法逆元總結
前言: 我們知道在模意義下的加減乘運算都是具有封閉性的,但除法確是例外,所以我們就要找一種在模意義下代替除法運算的東西 想看程式碼的在最下方 定義: 如果有ab≡1(modp),則稱b是mod p意義下a的乘法逆元。記b=inv(a)或b=a−1(定
【題解】POJ 1845 Sumdiv
bug stream register tro 質數 urn 數列 cloc 相關 【題目大意】 給定\(A\)和\(B\),求\(A^B\)的所有約數之和,對\(9901\)取模。 (對於全部數據,\(0<= A <= B <=50,000,000\))
二叉樹的建立和遍歷(遞迴建樹&層序遍歷建樹)
#include<stdio.h>#include <cstdlib>#include <iostream>#include <stack>#include<queue>using namespace std; //二叉樹定義typedef cha
01揹包問題:回溯法和限界分支、遞迴和迭代方式
01揹包問題 遞迴方式模板: void backtrack(int t){ if(t > n) output(x); else{ for(int i = f(n,t); i <= g(n,t);i++){ x[t
poj 1664放蘋果(轉載,不詳細,勿點)(遞迴)
題目和別人的解析傳送門 我的程式碼 #include<bits/stdc++.h> using namespace std; int f(int m,int n) { if(n==0) return 0; if(m==0||m==1) return 1;
lombok和JPA的死遞迴
這是一個Bug JPA我覺得是一個封裝的很棒的框架,至於說很臃腫這個事,我覺得在需求需要很多複雜查詢的時候,不適合用JPA。因為它的封裝太棒了,以致於增強了表和實體類之間的耦合。不過在一些簡單查詢的部分,它真的太好用了。 而關於這個Bug,就不單單是JPA自身的問題了,究其原因是我在
線索二叉樹和中序非遞迴遍歷線索化後的二叉樹
//線索二叉樹 #include<stdio.h> #include<malloc.h> #include<process.h> #define OVERFLOW -2 //二叉樹的二叉線索儲存結構 enum PointerTag{Lin
類和結構體的遞迴定義
1.類的遞迴定義有兩個類這樣定義:Subject.h 標頭檔案如下:#ifndef SUBJECT_H#define SUBJECT_H#include <iostream>#include "Observer.h"class Subject{public:
Leetcode---在排序陣列中查詢元素的第一個和最後一個位置--遞迴
在排序陣列中查詢元素的第一個和最後一個位置 題目連結:在排序陣列中查詢元素的第一個和最後一個位置 思路: 本題就是二分搜尋的變形,二分搜尋是找到一個數組中存在的目標數值的下標,這裡是尋找目標數值的起始和終點位置 處理方法只需要稍加改變,找到mid下標 該位置值
easyUI的tree和treeGrid不需要遞迴取,有內建的成樹欄位
$.fn.treegrid.defaults.loadFilter = function (data, parentId) { var opt = $(this).data().treegrid.options; var idFiled, parentField; if (opt.p
連結串列逆序(遞迴法)
來自SO,貌似沒啥實際應用,但是思路不錯,留存。 #include <Windows.h> #include <iostream> using namespace std; struct node { int data; node* next;
資料結構——二叉樹的建立和遍歷(遞迴建樹&層序遍歷建樹)
資料結構作業模板存檔 #include<stdio.h> #include <cstdlib> #include <iostream> #include <stack> #include<queue&g
二叉樹插入和刪除操作的遞迴實現(c語言)
連結串列和陣列是最常見的資料結構,對於資料結構來說,查詢(Find),最大最小值(FindMin,FindMax),插入(Insert)和刪除(Delete)操作是最基本的操作。對於連結串列和陣列來說,這些操作的時間界為O(N),其中N為元素的個數。陣列的插入和刪除需要對其他