zoukankan      html  css  js  c++  java
  • NOIP模拟测试7「方程的解·visit」

     visit

    由于一些不可预知的错误导致我一直WA 错误最后说

    思路

    方案一

    假设终点在出发点右上方(这样假设只是为了方便)

    假设向左走了a步,向右下了b布,那么相应的我们要向右走m+a,向上n+b步

    总步数t 所以由多重集方案数可得

    $ frac{t !}{a ! imes b! imes (n+a)! imes (m+b)!}$

    这种方法要特殊处理

    方案二

    假设向上下一共走了i步

    如果i超出了范围我们要往回走(i-n)/2步(自己在纸上画一下)

    然后如果i-n除不开2那么这种情况是无解的

    左右同理

    得到式子

    $ans=sumlimits_{i=n 2|i-n}^{t-m} C_{t}^{i} imes C_{i}^{frac{i-n}{2} } imes C_{t-i}^{frac{t-i-m}{2}}$

    因为模数不一定为质数,但是几个质数乘积,所以我们要用先求出来分别模这几个质数结果,然后CRT合并

    我犯的错误

    我一开始看了吴迪的式子($ frac{t !}{a ! imes b! imes (n+a)! imes (m+b)!}$),他的式子会发生另外一些不可预知的错误,模小数时他的式子会爆炸($n!$中$n$比模数大时会发生另外一些错误)

    然后我一开始没发现这个错误,发现他的式子跟我的差不多就开始打了,然后我就挂了。

    然后我又自己推了一个式子用线性求逆元发现还是一直WA

    最后wwb调了很长时间还是WA 最后进行了一番大改终于A了。

    我用老套路控制变量(用A的代码一段一段替换WA的代码)发现线性求逆元爆炸了。

    这里线性求逆元并不是我写错了,或者推错了

            jie[0]=1;
            ni[0]=1;
            for(ll j=1;j<=t;j++)
                jie[j]=jie[j-1]*j%w[i];
            ni[t]=meng(jie[t],w[i]-2,i);
            for(ll j=t-1;j>=1;j--)
                ni[j]=ni[j+1]*(j+1)%w[i];

    观察这段代码,首先如果jie里的j比w大那么他的阶乘取完模之后都为0,(前面有不是0的阶乘)

    而ni 从t开始算的话如果最后一位为0那么这样递推算出来所有的逆元都为0

    然而ni<w的一部分不是0 所以就错了

    警醒

    代码

    #include<bits/stdc++.h>
    #define ll long long
    #define A 2000000
    #define py printf("f**k
    ")
    ll a[A],b[A],k,p,n,m,t,num=0;
    ll w[A],q[A],v[A],jie[A],ni[A];
    ll exgcd(ll a,ll b,ll &x,ll &y){
        if(b==0){
            x=1;
            y=0;
            return a;
        }
        ll gcd=exgcd(b,a%b,x,y);
        ll t=x;
        x=y;
        y=t-a/b*y;
        return gcd;
    }
    void getprime(ll x){
        for(ll i=2;i<=sqrt(x);i++)
        {
            if(x%i==0)
            {
                while(x%i==0)
                {
                    x=x/i;
                }
                w[++w[0]]=i;
            }
        }
        if(x!=1) w[++w[0]]=x;
        return ;
    }
    ll meng(ll x,ll k,ll cix){
        ll ans=1;
        for(;k;k>>=1,x=x*x%w[cix])
            if(k&1)
                ans=ans*x%w[cix];
        return ans;
    }
    ll china(){
        ll x,y,a=0,m,n=1;
        for(ll i=1;i<=w[0];i++)
            n*=w[i];
        for(ll i=1;i<=w[0];i++){
            m=n/w[i];
            exgcd(w[i],m,x,y);
            y%=w[i];
            a=(a+y*m*b[i])%n;
        }
        return (a+n)%n;
    }
    ll jic(ll n,ll m,ll cix){
        if(m>n) return 0;
        if(m==0) return 1;
        return jie[n]%w[cix]*meng(jie[m]*jie[n-m]%w[cix],w[cix]-2,cix)%w[cix];
    }
    ll lucas(ll n,ll m,ll cix){
        if(m>n)return 0;
        if(n==0)return 1;
        return jic(n%w[cix],m%w[cix],cix)*lucas(n/w[cix],m/w[cix],cix)%w[cix];
    }
    using namespace std;
    int main()
    {
        scanf("%lld%lld",&t,&p);
        scanf("%lld%lld",&n,&m);
        getprime(p);
        n=abs(n),m=abs(m);
        for(ll i=1;i<=w[0];i++){
            jie[0]=1;
            ni[0]=1;
            for(ll j=1;j<=t;j++)
                jie[j]=jie[j-1]*j%w[i];
            ni[t]=meng(jie[t],w[i]-2,i);
            for(ll j=t-1;j>=1;j--)
                ni[j]=ni[j+1]*(j+1)%w[i];
            for(ll j=n;j<=t-m;j++){
                if((j-n)%2) continue;
                if((t-j-m)%2) continue;
                ll t1=lucas(t,j,i),t2=lucas(j,(j-n)/2,i),t3=lucas(t-j,(t-j-m)/2,i);
                b[i]=(b[i]+t1*t2%w[i]*t3)%w[i];
            }
        }
        cout<<china()<<endl;    
    }

    方程的解

    各种傻逼特判,判错一个就40

    思路:

    思路有两种

    一,

    首先求出来左右边界然后拿右边界减左边界,非常简单,按照解方程的方法求即可

    代码(我没打出来一直40分,这里是nc的代码)

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    ll T,a,b,c,x,y;
    ll exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(b==0)
        {
            x=1;y=0;
            return a;
        }
        ll d=exgcd(b,a%b,x,y);
        ll tmp=x;
        x=y;
        y=tmp-a/b*y;
        return d;
    }
    int main()
    {
        scanf("%lld",&T);
        while(T--)
        {
            scanf("%lld%lld%lld",&a,&b,&c);
            ll d=exgcd(a,b,x,y);
            ll l=floor(1.0*c/b*(-x)),r=ceil(1.0*c/a*y);
            ll k=r-l-1;
            if(a==0&&b==0)
            {
                if(c==0)
                {
                    puts("ZenMeZheMeDuo");
                    continue;
                }
                else
                {
                    puts("0");
                    continue;
                }
            }
            if((c%d))
            {
                puts("0");
                continue;
            }
            if(1LL*a*b<0)
            {
                puts("ZenMeZheMeDuo");
                continue;
            }
            if(a==0)
            {
                if(1LL*b*c>0) puts("ZenMeZheMeDuo");
                else puts("0");
                continue;
            }
            if(b==0)
            {
                if(1LL*a*c>0) puts("ZenMeZheMeDuo");
                else puts("0");
                continue;
            }
            if(k<0)
            {
                puts("0");
                continue;
            }
            if(k>65535) puts("ZenMeZheMeDuo");
            else printf("%lld
    ",k);
        }
        return 0;
    }
    二,

    求出来ymax 再求出来ymin 再/a

    非常简单的思路(虽然我觉得第一个更简单)

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define A 100000
    ll a,b,x,y,c,t;
    ll exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(b==0)
        {
            x=1;y=0;
            return a;
        }
        ll c=exgcd(b,a%b,x,y);
        ll z=x;
        x=y;y=z-y*(a/b);
    //    printf("x=%lld y=%lld
    ",x,y);
        return c; 
    }
    int main()
    {
    //    freopen("data.in","r",stdin);
    //    freopen("data.out","w",stdout);
        scanf("%lld",&t);
        while(t--)
        {
            scanf("%lld%lld%lld",&a,&b,&c);
    //        printf("a=%lld b=%lld
    ",a,b);
            x=0,y=0;
            if(a<0&&b<0) a=-a,b=-b,c=-c;        
            if(a==0)
            {
                if((b<=0&&c>=0)||(b>=0&&c<=0))
                {
                    printf("0
    ");
                    continue;
                }
                else if(c%b){
                    printf("0
    ");
                    continue;
                }
                else{printf("ZenMeZheMeDuo
    ");continue;};
            }
            if(b==0)
            {
                if((a<=0&&c>=0)||(a>=0&&c<=0))
                {
                    printf("0
    ");
                    continue;
                }
                else if(c%a){
                    printf("0
    ");
                    continue;
                }
                else {printf("ZenMeZheMeDuo
    ");continue;};
            }
            ll g=exgcd(a,b,x,y),ans=0;
            x*=c/g,y*=c/g;
    //        printf("%lld %lld a=%lld b=%lld c=%lld
    ",x,y,a,b,c);
            if(c%g){printf("0
    ");continue;}
            if(a*b<0)
            {printf("ZenMeZheMeDuo
    ");continue;}
    
            a/=g,b/=g,c/=g;x%=b;
            while(x<=0) x+=b;
            y=(c-a*x)/b;
            ll y2=y%a;
            while(y2<=0) y2+=a;
            ans=(y-y2)/a+1;
            if(y2>y) ans=0;
    //        printf("y=%lld y2=%lld x=%lld c=%lld
    ",y,y2,x,c);
            if(ans<=65535)
                printf("%lld
    ",ans);
            else
                printf("ZenMeZheMeDuo
    ");
        }
    }

    题目思路还是挺简单的就是一些恶心的特判

    特判

    首先$c mod gcd!=0$时无解

    然后$a,b$异号时无穷多解

    $a$为$0$,$b$为$0$,$c$为$0$时无穷多解

    $a$为$0$ $b$为$0$ $c$不为$0$ 无解

    $ymax<ymin$无解

    $a==0$ $  b,c$异号无解

    $a==0$ $  b,c$同号无穷多解

    $b==0$ $  a,c$异号无解

    $b==0$ $  a,c$同号无穷多解

    我已没有下降的余地
  • 相关阅读:
    windows下python开发环境搭建
    看看两年前的我
    网络函数[00]函数总述
    网络函数[04]connect解析
    网络函数[08]网络读取函数解析
    网络函数[01]套接口地址图解
    网络函数[13]
    网络函数[07]accept解析
    网络函数[10]shutdown解析
    网络函数[14]
  • 原文地址:https://www.cnblogs.com/znsbc-13/p/11227871.html
Copyright © 2011-2022 走看看