1. 程式人生 > >poj 1845 Sumdiv 數論--等比數列和(逆元或者遞迴)

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;

lombokJPA的死

這是一個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的treetreeGrid不需要取,有內建的成樹欄位

$.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為元素的個數。陣列的插入和刪除需要對其他