1. 程式人生 > >【數論】2018國慶三校聯考D4T2

【數論】2018國慶三校聯考D4T2

題意:

很簡單,給出正整數 n ,求出 n! 在十進位制表示下的從最末非零位開始的總共 k 位。
n 1 0 18 , k

3 n\leq 10^{18},k\leq 3


分析:

有一個暴力的想法:直接從1列舉到N,然後把每個數的2的因數和5的因數除去,剩餘部分乘起來 m o d  

1 0 k mod\ 10^k
然後我們剩下的,沒有乘進去的2的整次冪肯定不比5的整次冪少。所以只要把多去掉的2的整次冪乘回來即可。

現在考慮如何使這個過程更加高效:
顯然,我們可以很容易求出 n

! ( m o d   5 ) n!(mod\ 5) ,並且可以求出 n ! n! 中5的整次冪有多少。

方法很簡單: n ! = 4 ! n 5 ( n   m o d   5 ) ! n 5 ! ( m o d   5 ) n!=4!^{\lfloor \frac n 5\rfloor}*(n \ mod\ 5)!*\lfloor \frac n 5\rfloor!(mod\ 5)
定義 c n t ( n ) cnt(n) 表示 n ! n! 中所含的5的整次冪
cnt(n)= c n t ( n 5 ) + n 5 cnt(\lfloor\frac n 5\rfloor)+\lfloor\frac n 5\rfloor

這兩個都是用的一個方法:把所有5的倍數和非5的倍數分開來,然後5的倍數遞迴處理。

現在問題就是:如何除去2相應的次冪?由於模數是 1 0 k 10^k ,所以2是沒有逆元的。

這裡可以再套用中國剩餘定理:分別計算答案模 2 k 2^k 5 k 5^k 之後的值。然後組合起來作為 1 0 k 10^k 之後的答案。

顯然,答案在模 2 k 2^k 下的答案一定為0(n極小的情況特判)

於是對模 5 k 5^k ,2就有逆元了,所以可以直接除去相應個數的2

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
typedef long long ll;
struct BigInteger {
    typedef unsigned long long LL;
 
    static const int BASE = 100000000;
    static const int WIDTH = 8;
    vector<int> s;
 
    BigInteger& clean(){while(!s.back()&&s.size()>1)s.pop_back(); return *this;}
    BigInteger(LL num = 0) {*this = num;}
    BigInteger(string s) {*this = s;}
    BigInteger& operator = (long long num) {
        s.clear();
        do {
            s.push_back(num % BASE);
            num /= BASE;
        } while (num > 0);
        return *this;
    }
    BigInteger& operator = (const string& str) {
        s.clear();
        int x, len = (str.length() - 1) / WIDTH + 1;
        for (int i = 0; i < len; i++) {
            int end = str.length() - i*WIDTH;
            int start = max(0, end - WIDTH);
            sscanf(str.substr(start,end-start).c_str(), "%d", &x);
            s.push_back(x);
        }
        return (*this).clean();
    }
 
    BigInteger operator + (const BigInteger& b) const {
        BigInteger c; c.s.clear();
        for (int i = 0, g = 0; ; i++) {
            if (g == 0 && i >= int(s.size()) && i >= int(b.s.size())) break;
            int x = g;
            if (i < int(s.size())) x += s[i];
            if (i < int(b.s.size())) x += b.s[i];
            c.s.push_back(x % BASE);
            g = x / BASE;
        }
        return c;
    }
    BigInteger operator - (const BigInteger& b) const {
        BigInteger c; c.s.clear();
        for (int i = 0, g = 0; ; i++) {
            if (g == 0 && i >= int(s.size()) && i >= int(b.s.size())) break;
            int x = s[i] + g;
            if (i < int(b.s.size())) x -= b.s[i];
            if (x < 0) {g = -1; x += BASE;} else g = 0;
            c.s.push_back(x);
        }
        return c.clean();
    }
    BigInteger operator * (const BigInteger& b) const {
        int i, j; LL g;
        vector<LL> v(s.size()+b.s.size(), 0);
        BigInteger c; c.s.clear();
        for(i=0;i<int(s.size());i++) for(j=0;j<int(b.s.size());j++) v[i+j]+=LL(s[i])*b.s[j];
        for (i = 0, g = 0; ; i++) {
            if (g ==0 && i >= int(v.size())) break;
            LL x = v[i] + g;
            c.s.push_back(x % BASE);
            g = x / BASE;
        }
        return c.clean();
    }
    BigInteger operator / (const ll b) const {
        BigInteger c;
		c.s.resize(20); 
	    ll x=0;
		for(int i=int(s.size())-1;i>=0;i--){
			x=x*BASE+s[i];
			c.s[i]=x/b;
			x=x%b;
		}    
	    return c.clean();;  
    }
    ll operator % (const ll b) const { //???????
        BigInteger c = *this;
        ll m=0;
        for (int i = s.size()-1; i >= 0; i--) {
            m = (m*BASE + s[i])%b;
        }
        return m;
    }
    int bsearch(const BigInteger& b, const BigInteger& m) const{
        int L = 0, R = BASE-1, x;
        while (1) {
            x = (L+R)>>1;
            if (b*x<=m) {if (b*(x+1)>m) return x; else L = x;}
            else R = x;
        }
    }
    BigInteger& operator += (const BigInteger& b) {*this = *this + b; return *this;}
    BigInteger& operator -= (const BigInteger& b) {*this = *this - b; return *this;}
    BigInteger& operator *= (const BigInteger& b) {*this = *this * b; return *this;}
 
    bool operator < (const BigInteger& b) const {
        if (s.size() != b.s.size()) return s.size() < b.s.size();
        for (int i = s.size()-1; i >= 0; i--)
            if (s[i] != b.s[i]) return s[i] < b.s[i];
        return false;
    }
    bool operator >(const BigInteger& b) const{return b < *this;}
    bool operator<=(const BigInteger& b) const{return !(b < *this);}
    bool operator>=(const BigInteger& b) const{return !(*this < b);}
    bool operator!=(const BigInteger& b) const{return b < *this || *this < b;}
    bool operator==(const BigInteger& b) const{return !(b < *this) && !(b > *this);}
};
 
istream& operator >> (istream& in, BigInteger& x) {
    string s;
    if (!(in >> s)) return in;
    x = s;
    return in;
}
BigInteger n;
ll ans=1;
ll mod[5]=
            
           

相關推薦

數論2018國慶D4T2

題意: 很簡單,給出正整數 n ,求出 n! 在十進位制表示下的從最末非零位開始的總共 k 位。 n ≤

DP2018國慶

題意: 在N個格子之間,放入D-1個隔板(可以重合),要求每兩個相鄰隔板之間距離不超過M。求方案數。 N,M≤2000N,M\leq 2000N,M≤2000 D≤1012D\leq 10^{12}D≤

平衡樹2018國慶D3T3

分析: #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<assert.h> #def

資料結構線段樹2018國慶D5T3

題意: 分析: 有一個顯然的暴力方法: 對每個詢問,從左往右做一次,記錄字首和,當某個位置字首和為負後,則刪去當前點。再從右往左做一次。 考慮使這個過程變得高效: 可以將詢問按左端點從右往左排序,然後用棧依次處理每個在從左往右考慮時是否需要刪除。 再利用線段樹,求

圖論2018國慶D5T2

分析: 題意非常醜陋。。。簡化出來就一句話:每個點有選中、未選中兩種狀態,現在給出一些矛盾關係,要求加入儘可能少的矛盾關係,使得沒有合法方案。 如此2sat的模型,顯然需要2sat的連邊方式。。。然後直接列舉每個位置選、不選是否合法即可。若不選合法,則考慮其練的邊是否有一個

DP概率與期望2018國慶D3T2

題意: 分析: #include<cstdio> #include<cstring> #include<algorithm> #include<cma

資料結構線段樹字串Hash2018國慶D4T3

題意: 分析: 題解見標籤 (不過這題有非正解方法可以卡過去。。我程式碼附在下面) 正解: #include<cstdio> #include<cstring> #inclu

DP1017T3

題意: 分析: 考場上這題做了我兩個小時。。。果然第一步都錯了。。。 首先,所謂的絕對值其實可以用最優性忽略!!!! 即:|a-b|=max(a-b,b-a) 所以,不必考慮到底誰大誰小,在最優策略中,一定是合法的。 然後就很簡單了:每一個位置的貢獻分別可能為2,0,-

DP1017T2

分析 非常水的np狀態轉移的題 定義 D P [

莫隊連結串列1015T3

題意: 分析: 啊啊啊啊我發明的演算法居然以前有過。。。。https://blog.csdn.net/qq_34454069/article/details/80184286 方法其實很簡單。。。首先,把所有未加入的點放在一個雙向連結串列裡。 然後,每次插入一

10.24點亮

@點亮@ @問題描述@ @分析1 - 隨機的性質@ @分析2 - 利用性質@ @演算法細節@ @程式碼@ @證明(過於枯燥)@ @[email protected] @

[jzoj3252] GDOI炸彈(經典DP)

Problem 給出一個網格圖,由 01 01

組合數學DP 10.15 —— Chess

題目描述 dirty 在一個棋盤上放起了棋子。 棋盤規格為 n ∗ m,他希望任意一個 n ∗ n 的區域內都有 K 個棋子。dirty 很快就放置好了一 個滿足條件的棋盤方案,但是他認為這樣過於簡單了

圖論DFS10.20T2

題意 尋找有多少條邊滿足:圖中所有奇環都包含這條邊,且這條邊不屬於任何偶環 分析: 最後一個性質好坑。。。一直在想那個性質結果T3都沒來得及做… 直接建一個DFS樹。因為是無向圖,所以只存在樹邊和返祖

比賽題解FJ NOIP 四 2017 Round 7

快速 高度 str height size 都是 png logs h+ 此次比賽為廈門一中出題。都是聚勞,不敢恭維。 莫名爆了個0,究其原因,竟然是快讀炸了……很狗,很難受。 話不多說,來看看題: 【T1】 題意: 樣例: PS:1<=h[i]<=1000

NOIP2016提高A組五4label

BE sdn 表示 urn con next LG 對稱 class 題目 題目 20%算法 設\(f_{i,j}\)表示第i個節點選了j這個權值的方案數。 顯然轉移方程為,\[f_{i,j}=\Pi_{v=son(i)}(\sum_{k=1}^{j-k}f_{v,k}+

NOIP2016提高A組五4ksum

分析 -s down wap algo include CA namespace LG 題目 分析 發現,當子段[l,r]被取了出來,那麽[l-1,r]、[l,r+1]一定也被取了出來。 那麽,首先將[1,n]放入大頂堆,每次將堆頂的子段[l,r]取出來,因為它是堆頂,所

NOIP2016提高A組五4square

TP space net AS noip getch 分享 OS 什麽 題目 分析 首先,設\(f_{i,j}\)表示最大的以(i,j)為左下角的正方形的邊長。 轉移顯然,\(f_{i,j}=\max(f_{i-1,j},f_{i,j-1},f_{i-1,j-1})+1\

LOJ#2479. 「九省 2018」制胡竄

題解 老了,國賽之前敲一個字尾樹上LCT和線段樹都休閒的很 現在後綴樹上線段樹合併差點把我寫死 主要思路就是字尾樹+線段樹合併+容斥,我相信熟練的OIer看到這已經會了 但就是不想寫 但是由於我過於老年化,我還是決定記錄一下我的思路 我用字尾自動機建的字尾樹,所以是反串的字尾樹,我考慮的都是

題解Luogu P4363 [九省2018]一雙木棋chess

原題傳送門 這道題珂以輪廓線dp解決 經過推導,我們珂以發現下一行的棋子比上一行的棋子少(或等於),而且每一行中的棋子都是從左向右依次排列(從頭開始,中間沒有空隙) 所以每下完一步棋,棋盤的一部分是有棋子的,另一部分是沒棋子的 那麼,我們就珂以用一條輪廓線來表示有棋子的部分和沒棋子的部分的分界線 我