JZOJ5810. 【NOIP提高A组模拟2018.8.13】简单的玄学

这里写图片描述

Data Constraint

对于 10% 的数据,nm < 16;
对于 30% 的数据,nm < 64;
对于 50% 的数据,nm ≤ 10^3;
对于 70% 的数据,m ≤ 10^6;
对于 100% 的数据,1 ≤ n ≤ 10^18 , 2 ≤ m ≤ 10^18

题解

要求至少有两个相同的,的确不好求。
正难则反,求完全不同的方案数,然后用1减。

那么,完全不同的方案数 C 2 n m
考虑约分,先给出一个结论,X!里面有因子2的个数是X-X在二进制中1的个数。
( 2 n ) ! ( 2 n m + 1 ) ! 前面一个数在二进制中1的个数很好求,
关键是 ( 2 n m + 1 ) ! ,很显然,开头一段都是1,然后从m的第一位带m的最后一个1 之前,都是跟m去反,m的最后一个1对于的那一位就是1,最后的都是0。
这个很好理解。

约分之后,就可以直接O(m)地乘,如果m大于模数,最后取模之后就是0了。

code

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long
#define N 100003
#define M 103
#define db double
#define P putchar
#define G getchar
using namespace std;

const int mo=1000003;

ll ksm(ll x,ll y) 
{
    ll s=1;x=x%mo;
    for(;y;x=x*x%mo,y>>=1)
        if(y&1)s=s*x%mo;
    return s;
}

ll n,m,ss,s,nn,mm,p,t,ny,ans,sm;

int main()
{
    freopen("random.in","r",stdin);
    freopen("random.out","w",stdout);

    scanf("%lld%lld",&n,&m);
    s=1;p=mo-1;nn=n%p;mm=m%p;
    for(int i=1;i<=n;i++)
    {
        s=s<<1;
        if(s>m)break;
    }
    if(s<m)
    {
        printf("1 1\n");
        return 0;
    }

    for(s=m,sm=ss=0;s;s>>=1)
    {
        if(ss==0)
        {
            if(s&1)ss=1;
        }
        else ss=ss+!(s&1);
        sm++;
    }
    ss=n-sm+ss;
    t=m+ss-1;t=t%p;
    s=ksm(2,(nn*mm%p-t+p)%p);

    if(m>=mo)
    {
        printf("%lld %lld\n",s,s);
        return 0;
    }

    ans=1;ss=ksm(2,nn)-m+1;
    ss=(ss+mo)%mo;
    for(int i=1;i<=m;i++)
        ans=ans*ss%mo,ss++;
    ny=ksm(2,p-1);
    ans=ans*ksm(ny,t)%mo;

    ans=(s-ans+mo)%mo;

    printf("%lld %lld",ans,s);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/lijf2001/article/details/81670528
今日推荐