zoukankan      html  css  js  c++  java
  • bzoj4807 車

    题目大意:

    Description

    众所周知,車是中国象棋中最厉害的一子之一,它能吃到同一行或同一列中的其他棋子。車跟車显然不能在一起打
    起来,于是rly一天又借来了许多许多的車在棋盘上摆了起来……他想知道,在N×M的矩形方格中摆最多个数的車
    使其互不吃到的情况下方案数有几种。但是,由于上次摆炮摆得实在太累,他为了偷懒,打算增加一个条件:对于
    任何一个車A,如果有其他一个車B在它的上面(車B行号小于車A),那么車A必须在車B的右边(車A列号大于車B)
    棋子都是相同的。

    Input

    一行,两个正整数N和M。
    N<=1000000,M<=1000000

    Output

    一行,输出方案数的末尾50位(不足则直接输出)。

    Sample Input

    2 2

    Sample Output

    1
     

    Solution

    题目看起来狠厉害的样子,但是仔细发现,
    由于增加的那个条件非常的苛刻,并且車不能互相攻击到。
    所以,最优情况下,一定是左上到右下斜着放下去。
    假设n>=m
    发现,在最优情况下,m列一定放满了,最优答案就是m。
    然后,n行可能没有放满,所以,方案数就相当于,
    在n行里面选择m行放置这m个棋子的方案数。
    这就是C(n,m)呀!!
     
    所以,方案数就是C(n,m)
     
    但是,n<=1e6,输出又要保留50位,要用高精!!
    首先,我们肯定不能按照一般运算顺序,
    即先算n!,再算m!,(n-m)!。
    就算是你愿意高精除以低精,
    但是n!就已经位数估计7e6了。再循环n次?不可能。
     
    但是,最后就要保留50位啊,那n!就每次取个余数,然后再用这个余数除以m!
    纯粹瞎搞。取余运算哪里有这个运算律??
     
    那么怎么办?
     
    发现,困扰的地方就是除法。
    如果只有乘法,没有除法,那么每次取最后50位就是可行的了。
     
    怎么避免除法?
     
    组合数一定是一个整数。所以n!的质因数分解后,肯定能消完分母的质因子分解情况。
    先化简一下:
    C(n,m)=(m+1)*.....*n/(1*....*(n-m)
    线性筛素数。
    把分母1~n-m依次用质数根号n质因数分解。
    每个质因子维护一个桶,记录在分母中出现的次数。
    之后,循环分子i,
    把分子质因数分解,用分母中的质因子桶能消除一些质因子就消除。
    最后留下的i可以高精乘进去。
    (可以采用压位高精,快10倍)
     
    虽然理论nsqrt(n),但是由于用质数分解更快,而且小数不用循环sqrt(n)次。
    所以O(能过)
     

    Code

    /**************************************************************
        Problem: 4807
        User: 20011023
        Language: C++
        Result: Accepted
        Time:1332 ms
        Memory:17892 kb
    ****************************************************************/
     
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1000000+5;
    const ll mod=1e10;
    int n,m;
    int k;
    int exi[N];
    int ans;
    int yue[N],has[N],cnt;
    int pri[N],tot;
    bool vis[N];
    void sieve(){
        for(int i=2;i<=n;i++){
            if(!vis[i]){
                pri[++tot]=i;
            }
            for(int j=1;j<=tot;j++){
                if(pri[j]*i>n) break;
                vis[pri[j]*i]=1;
                if(i%pri[j]==0) break;
            }
        }
    }
    void div1(int x){
        for(int i=1;pri[i]*pri[i]<=x;i++){
            while(x%pri[i]==0){
                exi[i]++;
                x/=pri[i];
            }
        }
        if(x>1){
            int k=lower_bound(pri+1,pri+tot+1,x)-pri;
            exi[k]++;
        }
    }
    int div2(int x){
        int now=x;
        for(int i=1;pri[i]*pri[i]<=x;i++){
            if(now%pri[i]==0){
                while(now%pri[i]==0){
                    if(exi[i]){
                        exi[i]--;
                        x/=pri[i];}
                    now/=pri[i];
                }
            }
        }
        if(now>1){
            int k=lower_bound(pri+1,pri+tot+1,now)-pri;
            if(exi[k]){
                exi[k]--;
                x/=pri[k];
            }
        }
        return x;
    }
    struct big{
        ll a[10];
        int cnt;
        void pre(){
            cnt=1,a[1]=1;
        }
        void mul(const ll &x){
            int ji=0;
            for(int i=1;i<=cnt;i++){
                a[i]=a[i]*x+ji;
                ji=a[i]/mod;
                a[i]%=mod;
            }
            if(ji){
                a[++cnt]=ji;
            }
            if(cnt>5) cnt=5;
        }
        void op(){
            while(a[cnt]==0&&cnt>1) cnt--;
            printf("%lld",a[cnt--]);
            while(cnt)printf("%010lld",a[cnt--]);
        }
    }A;
    void wrk(){
        A.pre();
        for(int i=m+1;i<=n;i++){
            int re=div2(i);
            A.mul(re);
        }   
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        if(m>n) swap(n,m);
        if(n==m){
            printf("1");return 0;
        }
        else if(n==1){
            printf("%d",m);return 0;
        }
        else if(m==1){
            printf("%d",n);return 0;
        }
        sieve();
        for(int i=1;i<=n-m;i++){
            div1(i);
        }
        wrk();
        A.op();
        return 0;
    }

    Conclusion

    质因数分解往往可以起到意想不到的优化,因为一个数的分解呈现了它的本质。

    决定了一切的乘除,gcd,找因数等运算。

    1.乘除,本质上是质因子次数的加减。

    2.gcd本质上是所有质因子次数取min再相乘。lcm则取max

    3.因数的产生本质上是一个乘法原理。质因子次数的选择也决定了因数的数值。

    并且,处理一些gcd问题时,质因数分解的考虑方式也值得去尝试。

  • 相关阅读:
    TTL电平和CMOS电平总结
    掩码
    关于Autosar中DCM(14229UDS)模块的理解
    Diagnostic Trouble Code诊断故障码
    eclipse搭建android开发环境
    在ubuntu下安装zookeeper
    redis的windows版本下载地址及windows的客户端工具
    最简单的启动并连接一个redis的docker容器
    转:Redis介绍及常用命令大全
    redis常用命令
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9524307.html
Copyright © 2011-2022 走看看