【数论】hdu5768 Lucky7(中国剩余定理)

版权声明:本文为博主原创文章,转载请注明出处( ^_^ ) https://blog.csdn.net/miku23736748/article/details/52151044

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5768

题目大意:

给n组质数m和余数a,给定范围(x,y),求这之中能整除7的数s。

再排除其中满足  s%m=a的数,问有多少个这样的数。


解题思路:

看题意就知道,这是明显的容斥题,而m又都是质数,可以直接套用中国余数定理。

只要将(7,0)这组数加入余数方程组,就能得到每次的结果。


中国余数定理运算过程中会爆long long,所以要用快速乘。


容斥原理:

先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去


总和S=A+B+C-BnC-Anc-AnB+AnBnC(重叠奇数部分加,偶数部分减)


同余:

两个整数a、b,若它们除以整数m所得的余数相等,则称a与b对于模m同余或a同余于b模m
记作 a≡b (mod m)
读作 a同余于b模m,或读作a与b对模m同余。

中国余数定理(CRT):

用现代数学的语言来说明的话,中国剩余定理给出了以下的一元线性同余方程组:

{\displaystyle (S):\quad \left\{{\begin{matrix}x\equiv a_{1}{\pmod {m_{1}}}\\x\equiv a_{2}{\pmod {m_{2}}}\\\vdots \qquad \qquad \qquad \\x\equiv a_{n}{\pmod {m_{n}}}\end{matrix}}\right.}(S) : \quad \left\{ \begin{matrix} x \equiv a_1 \pmod {m_1} \\ x \equiv a_2 \pmod {m_2} \\ \vdots \qquad\qquad\qquad \\ x \equiv a_n \pmod {m_n} \end{matrix} \right.

有解的判定条件,并用构造法给出了在有解情况下解的具体形式。

中国剩余定理说明:假设整数m1m2, ... , mn其中任两数互质,则对任意的整数:a1a2, ... , an,方程组{\displaystyle (S)}(S)有解,

并且通解可以用如下方式构造得到:

  1. {\displaystyle M=m_{1}\times m_{2}\times \cdots \times m_{n}=\prod _{i=1}^{n}m_{i}}M = m_1 \times m_2 \times \cdots \times m_n = \prod_{i=1}^n m_i是整数m1m2, ... , mn的乘积,并设{\displaystyle M_{i}=M/m_{i},\;\;\forall i\in \{1,2,\cdots ,n\}}M_i = M/m_i, \; \; \forall i \in \{1, 2, \cdots , n\},即{\displaystyle M_{i}}M_{i}是除了mi以外的n − 1个整数的乘积。 
  2. {\displaystyle t_{i}=M_{i}^{-1}}t_i = M_i^{-1}{\displaystyle M_{i}}M_{i}{\displaystyle m_{i}}m_{i}数论倒数{\displaystyle t_{i}M_{i}\equiv 1{\pmod {m_{i}}},\;\;\forall i\in \{1,2,\cdots ,n\}.}t_i M_i \equiv 1 \pmod {m_i},  \; \; \forall i \in \{1, 2, \cdots , n\}.从假设可知,对任何i \in \{1, 2, \cdots , n\},由于\forall j \in \{1, 2, \cdots , n\}, \; j\neq i, \; \; \operatorname{gcd}(m_i, m_j) = 1,所以\operatorname{gcd}(m_i, M_i) = 1. 这说明存在整数t_{i}使得t_i M_i \equiv 1 \pmod {m_i}. 
  3. 方程组{\displaystyle (S)}(S)的通解形式为:{\displaystyle x=a_{1}t_{1}M_{1}+a_{2}t_{2}M_{2}+\cdots +a_{n}t_{n}M_{n}+kM=kM+\sum _{i=1}^{n}a_{i}t_{i}M_{i},\quad k\in \mathbb {Z} .}x = a_1 t_1 M_1 + a_2 t_2 M_2 + \cdots + a_n t_n M_n + k M= k M + \sum_{i=1}^n a_i t_i M_i, \quad k \in \mathbb{Z}. 在模{\displaystyle M}M的意义下,方程组{\displaystyle (S)}(S)只有一个解:{\displaystyle x=\sum _{i=1}^{n}a_{i}t_{i}M_{i}.}x = \sum_{i=1}^n a_i t_i M_i.
中国余数定理的模板如下:
int a[4], m[4];  
  
void extend_Euclid(int a, int b, int &x, int &y)  
{  
    if(b == 0)  
    {  
        x = 1;  
        y = 0;  
        return;  
    }  
    extend_Euclid(b, a % b, x, y);  
    int tmp = x;  
    x = y;  
    y = tmp - (a / b) * y;  
}  
  
int CRT(int a[],int m[],int n)  
{  
    int M = 1;  
    int ans = 0;  
    for(int i=1; i<=n; i++)  
        M *= m[i];  
    for(int i=1; i<=n; i++)  
    {  
        int x, y;  
        int Mi = M / m[i];  
        extend_Euclid(Mi, m[i], x, y);  
        ans = (ans + Mi * x * a[i]) % M;  
    }  
    if(ans < 0) ans += M;  
    return ans;  
}  

完整代码如下:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAXN 20

typedef  long long LL;
const LL mod = 1e9+7;

LL a[MAXN],m[MAXN];
LL pri[MAXN],moe[MAXN];

void exgcd(LL a,LL b,LL &d,LL& x,LL& y) {
    if(!b) {
        d=a;
        x=1;
        y=0;
    } else {
        exgcd(b,a%b,d,y,x);
        y-=x*(a/b);
    }
}

LL mul(LL a,LL b,LL mod) {
    a%=mod;
    LL ret=0;
    while(b) {
        if(b&1)ret=(ret+a)%mod;
        b>>=1;
        a=(a+a)%mod;
    }
    return ret;
}

LL china(int n,LL* a,LL *m) {
    LL M=1,d,y,x=0;
    for(int i=0; i<n; ++i)M*=m[i];
    for(int i=0; i<n; ++i) {
        LL w=M/m[i];
        exgcd(m[i],w,d,d,y);
        x=(x+mul(mul(y,w,M),a[i],M))%M;
    }
    return (x+M)%M;
}

int main() {
    //freopen("in.txt","r",stdin);
    int kase=0,n,T;
    scanf("%d",&T);
    while(T--) {
        LL l,r;
        scanf("%d",&n);
        cin>>l>>r;
        for(int i=0; i<n; ++i)
            cin>>pri[i]>>moe[i];
        LL len=(1<<n);
        LL ret=r/7-(l-1)/7;
        //对给出的几组质数排列组合,排除所有多余项(容斥)
        for(int i=1; i<len; ++i) {
            int cnt=0;
            LL cur=1;
            for(int j=0; j<n; ++j) {
                if(i&(1<<j)) {
                    m[cnt]=pri[j];
                    a[cnt]=moe[j];
                    cnt++;
                    cur*=pri[j];
                }
            }
            m[cnt]=7;
            a[cnt]=0;
            cur*=7;
            cnt++;
            LL tmp=china(cnt,a,m);
            LL sub=0;
            int poi=0;
            if(tmp>=l&&tmp<=r) {
                LL cha=r-tmp;
                sub=cha/cur+1;//从tmp开始到r为止,需要排出的数字个数
            }

            else if(tmp<l) {
                LL cha=l-tmp;
                tmp+=(cha/cur)*cur;//如果tmp小于l,则使tmp>=l
                if(tmp<l)tmp+=cur;
                if(tmp>=l&&tmp<=r) {
                    cha=r-tmp;
                    sub=cha/cur+1;
                }
            }

            //奇数加 偶数减(容斥)
            if(cnt&1)ret+=sub;
            else ret-=sub;
        }
        printf("Case #%d: %I64d\n",++kase,ret);
    }
    return 0;
}



阅读更多
换一批

没有更多推荐了,返回首页