• 组合数学总结


    ---恢复内容开始---

      又水过一个专题。。。教练说这题都不好做///

      进了组合数学,当年叱吒的DP居然成了暴力。。。一些神奇的操作令我大口吃屎大开脑洞。

      一些常写的代码

      

     1 int qpow(int x,int k)
     2 {
     3     int ans=1;
     4     for(;k;k>>=1,x=1ll*x*x%mod) if(k&1) ans=1ll*ans*x%mod;
     5     return ans;
     6 }
     7 long long C(int n,int m) {return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
     8 int main()
     9 {
    10     scanf("%d%d",&n,&k);fac[0]=1;
    11     for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
    12     inv[n]=qpow(fac[n],mod-2);
    13     for(int i=n;i;i--) inv[i-1]=1ll*inv[i]*i%mod;

      然后有一些常用的知识

      :乘法原理  加法原理  容斥原理  古典概型转组合数。。。 插空挡板等等

      看一下题目长什么样。。

      A.排队(自主研发) 这是个高考数学题 就是用插空法,但是要注意先考虑n 名男同学,m 名女同学和两名老师要排队,女生和老师都不能相邻,如果先让老师不邻,再考虑女生不相邻会算少,老师相邻时中间插女生会漏掉。

      

    #75880 #A. 排队 Accepted 100 674 ms 604 KiB starsing (平安) 2019-06-26 18:50:09
    #75800 #A. 排队 Wrong Answer 20 428 ms 608 KiB starsing (平安) 2019-06-26 15:58:51

      

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<string>
    using namespace std;
    int a[4000000],b[4000020],c[4000020],n,m;
    void ch(int a[],int i)
    {
        int x=0,j;
        for(j=1;j<=a[0];j++)
        {
            a[j]=a[j]*i+x;
            x=a[j]/10;
            a[j]%=10;
        }
        a[j]=x;
        while(a[j]>9)
        {
            a[j+1]=a[j]/10;
            a[j]%=10;
            j++;
        }
        while(a[j]==0&&j>1)
            j--;
        a[0]=j;
    }
    void add(int a[],int b[])
    {
        int i=1,x=0;
        while(i<=a[0]||i<=b[0])
        {
            //cout<<a[i]<<" "<<b[i]<<" ";
            c[i]=a[i]+b[i]+x;
            //cout<<c[i]<<endl;
            x=c[i]/10;
            c[i]=c[i]%10;
            i++;
        
        }
        if(x!=0&&i>1)
        {
            c[0]=i;
            c[i]=x;
        }
        else c[0]=i-1;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        if(m>n+3) return printf("0
    "),0;
        a[0]=a[1]=1;b[0]=b[1]=1;
        ch(a,n);ch(a,n+1);ch(b,(2*n+2)*m);
        for(int i=n-m+4;i<=n+3;i++) ch(a,i);
        for(int i=n-m+4;i<=n+2;i++) ch(b,i);
        add(a,b);
        for(int i=1;i<=n;i++) ch(c,i);
        for(int i=c[0];i;i--) printf("%d",c[i]);
        puts("");
    }
    //排队 题解
    /*
    g++ 1.cpp -o 1
    ./1
    1 1
    */
    View Code

      B.Perm排列计数(自主研发) Pi<Pi/2的1~n排列令我大口吃屎。。。居然是下标。。。。然后搞一个二叉树,左右子树不影响,递归DP一下(题眼),bobo说学过树的都应该想到。

      

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int n,p;
    int f[2000020],s[2000020],fac[2000020],inv[2000020];
    void dfs(int k)
    {
        s[k]=1;
        if((k<<1)<=n) dfs(k<<1);
        if((k<<1|1)<=n) dfs(k<<1|1);
        s[k]+=s[k<<1]+s[k<<1|1];
        //if(s[k]==1) f[k]=1
        f[k]=1ll*fac[s[k]-1]*inv[s[k<<1]]%p*inv[s[k]-s[k<<1]-1]%p;
        if(f[k]==0)f[k]=1;
        if(f[k<<1]) f[k]=1ll*f[k]*f[k<<1]%p;
        if(f[k<<1|1]) f[k]=1ll*f[k]*f[k<<1|1]%p;
        //cout<<k<<" "<<s[k]<<" "<<f[k]<<endl;
    }
    int qpow(int x,int k)
    {
        int ans=1;
        for(;k;k>>=1,x=1ll*x*x%p) if(k&1) ans=1ll*x*ans%p;
        return ans;
    }
    int main()
    {
        scanf("%d%d",&n,&p);fac[0]=1;
        for(int i=1;i<=n*2+1;i++) f[i]=1;
        for(int i=1;i<=n;i++) fac[i]=1ll*i*fac[i-1]%p;
        inv[n]=qpow(fac[n],p-2);
        for(int i=n;i>=1;i--) inv[i-1]=1ll*i*inv[i]%p;
        dfs(1);
        printf("%d
    ",f[1]);
    }
    /*
    g++ 1.cpp -o 1
    ./1
    20 23
    */
    View Code

      C.集合计数(颓了一半题解) 会做么?不会就好题。是在大小为n的集合中选任意个集合,交集元素为k的方案个数。

      我首先想到了把交集为k个提出来,然后求n-k个选交集为0,即f[n-k][0],可以从2^(2^(n-k))-1个减f[n-k][j]转移过来,递推n^2实现很简单就没打,然后想颓了一眼题解(容斥原理),但是难以理解,想了一天又去找DeepinC大神,发现其实是容斥集合的定义和本题的集合不太一样,容斥的集合是包含一个元素的所有方案组成的集合。。。容斥应该作为一个随想随用的算法,不要心理上耸掉,只要好好搞一个容斥系数就可以了

      

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int mn=1e6+20,mod=1e9+7;
    int f[mn],fac[mn],inv[mn],n,k;
    int qpow(int x,int k)
    {
        int ans=1;
        for(;k;k>>=1,x=1ll*x*x%mod) if(k&1) ans=1ll*ans*x%mod;
        return ans;
    }
    long long C(int n,int m) {return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
    int main()
    {
        scanf("%d%d",&n,&k);fac[0]=1;
        for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
        inv[n]=qpow(fac[n],mod-2);
        for(int i=n;i;i--) inv[i-1]=1ll*inv[i]*i%mod;
        long long ans=0,tmp=C(n,k),tp=1;n-=k;
        int op=(n&1)?-1:1;
        for(int i=n;~i;i--)
        {
            ans=(ans+op*C(n,i)*tp%mod)%mod;
            tp=(tp*tp%mod+2*tp%mod)%mod;
            op=-op;
        }
        ans=ans*tmp%mod;
        printf("%lld
    ",(ans+mod)%mod);
    }
    //for sigma i=0->n C(n,i)(2^(2^(n−i))−1)
    /*
    g++ 1.cpp -o 1
    ./1
    4 1
    */
    View Code

     

      D.DZY loves math 2(全颓题解) 劳资他娘的颓了题解还不会,只能等我变强之后在回过头再做。。。


      E.虔诚的母猪人墓主人 (暴力水过,正解算是颓了题解10个字) 这是个数据结构题,思维量很小,就想到一个离散化不影响答案,

      然后每一行有一段段的横区间,区间内左右组合数一定,要维护竖着的ΣC上*C下就行了,没sort调了3节课骂了无数的街。。

      

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #define ll long long
    using namespace std;
    const unsigned int mod=2147483647;
    struct point
    {
        int x,y;
        bool friend operator <(const point &a,const point &b)
        {
            if(a.x==b.x) return a.y<b.y;
            return a.x<b.x;
        }
    }t[100020];
    unsigned int x[100020],y[100020],lx[100020],ly[100020],n,m,nx,ny,w;
    unsigned int b[100020],s[100020],pre[100020];
    unsigned int a[100020],C[100020][15];
    vector<int> r[100020];
    int rd()
    {
        int s=0,w=1;
        char cc=getchar();
        while(cc<'0'||cc>'9') {if(cc=='-') w=-1;cc=getchar();}
        while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar();
        return s*w;
    }
    void add(int i,int w) {for(;i<=ny;i+=(i&(-i))) a[i]+=w;}
    unsigned int ask(int i)
    {
        unsigned int ans=0;
        for(;i;i-=(i&(-i))) ans+=a[i];
        return ans;
    }
    bool cmp(const int &a,const int &b)
    {
        return t[a].y<=t[b].y;
    }
    int main()
    {
        //freopen("25.in","r",stdin);
        n=rd(),m=rd();
        w=rd();
        for(int i=1;i<=w;i++)
        {
            x[i]=lx[i]=rd();
            y[i]=ly[i]=rd();
        }
        int k=rd();
        for(int i=0;i<=w;i++) C[i][0]=1;
        for(int i=1;i<=w;i++) 
            for(int j=1;j<=min(i,k);j++)
            C[i][j]=C[i-1][j]+C[i-1][j-1];
        sort(lx+1,lx+w+1);sort(ly+1,ly+w+1);
        nx=unique(lx+1,lx+w+1)-lx-1,ny=unique(ly+1,ly+w+1)-ly-1;
        for(int i=1;i<=w;i++)
        {
            t[i].x=lower_bound(lx+1,lx+nx+1,x[i])-lx;
            t[i].y=lower_bound(ly+1,ly+ny+1,y[i])-ly;
        }
        sort(t+1,t+w+1);
        for(int i=1;i<=w;i++)
        {
            r[t[i].x].push_back(i);
            pre[i]=s[t[i].y]++;
        }
        unsigned int ans=0;
        for(int i=1;i<=nx;i++)
        {
            //cout<<i<<endl;
            int len=r[i].size();
            sort(r[i].begin(),r[i].end(),cmp);
            if(i!=1)
            {
                for(int j=k;j<=len-k;j++)
                {
                    int ai=t[r[i][j]].y,bi=t[r[i][j-1]].y;
                    if(ai<bi) swap(ai,bi);
                    //if(ai==bi+1) continue;
                    ans+=(ask(ai-1)-ask(bi))*C[j][k]*C[len-j][k];
                    //printf("%d %d %d %d
    ",ask(ai-1)-ask(bi),j,C[j][k],ans);
                }
            }
            for(int j=0;j<len;j++)
            {
                int pr=pre[r[i][j]],nt=s[t[r[i][j]].y]-pr-1,ai=t[r[i][j]].y;
                int wi=C[pr+1][k]*C[nt][k]-ask(ai)+ask(ai-1);
                //printf("%d %d %d %d
    ",j,ask(ai)-ask(ai-1),pr,nt);
                //cout<<r[i][j]<<" "<<pr+1<<" "<<nt<<endl;
                add(ai,wi);
            }
        }
        ans&=mod;
        printf("%d
    ",ans);
    }
    /*
    n%(1<<k)=n&((1<<k)-1)
    g++ 1.cpp -o 1
    ./1
    5 6
    13
    0 2
    0 3
    1 2
    2 0
    1 3
    3 3
    2 1
    2 4
    2 5
    2 6
    3 2
    5 2
    4 3
    2
    */
    听说这是个省选题

      F. 地精部落    (半题解) 他们都用两维数组做得。。。这是个DP题,要找无连续3个数单调,即锯齿状的序列,问题出在维护信息过多很难P。。然后从状压打起,逐渐减少维护信息,然后再从长度为i,最后一位为k,是峰或谷的状态定义逐渐发现转移错误,后来经tdcp点拨,外加自己思考,发现第二维会转移重复,然后想到其实谁是谁无所谓,只要关心排名是多少就行了,第二维定义为排名为k的方案。

    一般的题解第一句话就是设XXX,转移XXX,然后代码。。。其实最关键的是状态的定义,定义对了转移就显然,状态的定义就要抓住题目的特殊条件,如果维护信息过多就先从状压想起,逐渐减少无用信息,一般就能想到正经状态了

      

    ---恢复内容结束---

      又水过一个专题。。。

      进了组合数学,当年叱吒的DP居然成了暴力。。。一些神奇的操作令我大口吃屎大开脑洞。

      一般有一些常写的代码

      

     1 int qpow(int x,int k)
     2 {
     3     int ans=1;
     4     for(;k;k>>=1,x=1ll*x*x%mod) if(k&1) ans=1ll*ans*x%mod;
     5     return ans;
     6 }
     7 long long C(int n,int m) {return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
     8 int main()
     9 {
    10     scanf("%d%d",&n,&k);fac[0]=1;
    11     for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
    12     inv[n]=qpow(fac[n],mod-2);
    13     for(int i=n;i;i--) inv[i-1]=1ll*inv[i]*i%mod;

      然后有一些常用的知识

      :乘法原理加法原理  容斥原理 

      看一下题目长什么样。。

      A.排队 这是个高考数学题 就是用插空法,但是要注意先考虑老师不相邻,在考虑女生不相邻会算少,老师相邻时中间插女生会漏掉。

      B.Per排列 Pi<Pi/2令我大口吃屎。。。居然是下标。。。。然后搞一个二叉树左右子树不影响(题眼)

      C.集合计数 这tm题日了我两天。。

    ---恢复内容结束---

      又水过一个专题。。。

      进了组合数学,当年叱吒的DP居然成了暴力。。。一些神奇的操作令我大口吃屎大开脑洞。

      一些常写的代码

      

     1 int qpow(int x,int k)
     2 {
     3     int ans=1;
     4     for(;k;k>>=1,x=1ll*x*x%mod) if(k&1) ans=1ll*ans*x%mod;
     5     return ans;
     6 }
     7 long long C(int n,int m) {return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
     8 int main()
     9 {
    10     scanf("%d%d",&n,&k);fac[0]=1;
    11     for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
    12     inv[n]=qpow(fac[n],mod-2);
    13     for(int i=n;i;i--) inv[i-1]=1ll*inv[i]*i%mod;

      然后有一些常用的知识

      :乘法原理加法原理  容斥原理 

      看一下题目长什么样。。

      A.排队(自主研发) 这是个高考数学题 就是用插空法,但是要注意先考虑n 名男同学,m 名女同学和两名老师要排队,女生和老师都不能相邻,如果先让老师不邻,再考虑女生不相邻会算少,老师相邻时中间插女生会漏掉。

      

    #75880 #A. 排队 Accepted 100 674 ms 604 KiB starsing (平安) 2019-06-26 18:50:09
    #75800 #A. 排队 Wrong Answer 20 428 ms 608 KiB starsing (平安) 2019-06-26 15:58:51

      

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<string>
    using namespace std;
    int a[4000000],b[4000020],c[4000020],n,m;
    void ch(int a[],int i)
    {
        int x=0,j;
        for(j=1;j<=a[0];j++)
        {
            a[j]=a[j]*i+x;
            x=a[j]/10;
            a[j]%=10;
        }
        a[j]=x;
        while(a[j]>9)
        {
            a[j+1]=a[j]/10;
            a[j]%=10;
            j++;
        }
        while(a[j]==0&&j>1)
            j--;
        a[0]=j;
    }
    void add(int a[],int b[])
    {
        int i=1,x=0;
        while(i<=a[0]||i<=b[0])
        {
            //cout<<a[i]<<" "<<b[i]<<" ";
            c[i]=a[i]+b[i]+x;
            //cout<<c[i]<<endl;
            x=c[i]/10;
            c[i]=c[i]%10;
            i++;
        
        }
        if(x!=0&&i>1)
        {
            c[0]=i;
            c[i]=x;
        }
        else c[0]=i-1;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        if(m>n+3) return printf("0
    "),0;
        a[0]=a[1]=1;b[0]=b[1]=1;
        ch(a,n);ch(a,n+1);ch(b,(2*n+2)*m);
        for(int i=n-m+4;i<=n+3;i++) ch(a,i);
        for(int i=n-m+4;i<=n+2;i++) ch(b,i);
        add(a,b);
        for(int i=1;i<=n;i++) ch(c,i);
        for(int i=c[0];i;i--) printf("%d",c[i]);
        puts("");
    }
    //排队 题解
    /*
    g++ 1.cpp -o 1
    ./1
    1 1
    */
    View Code

      B.Perm排列计数(自主研发) Pi<Pi/2的1~n排列令我大口吃屎。。。居然是下标。。。。然后搞一个二叉树,左右子树不影响,递归DP一下(题眼),bobo说学过树的都应该想到。

      

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int n,p;
    int f[2000020],s[2000020],fac[2000020],inv[2000020];
    void dfs(int k)
    {
        s[k]=1;
        if((k<<1)<=n) dfs(k<<1);
        if((k<<1|1)<=n) dfs(k<<1|1);
        s[k]+=s[k<<1]+s[k<<1|1];
        //if(s[k]==1) f[k]=1
        f[k]=1ll*fac[s[k]-1]*inv[s[k<<1]]%p*inv[s[k]-s[k<<1]-1]%p;
        if(f[k]==0)f[k]=1;
        if(f[k<<1]) f[k]=1ll*f[k]*f[k<<1]%p;
        if(f[k<<1|1]) f[k]=1ll*f[k]*f[k<<1|1]%p;
        //cout<<k<<" "<<s[k]<<" "<<f[k]<<endl;
    }
    int qpow(int x,int k)
    {
        int ans=1;
        for(;k;k>>=1,x=1ll*x*x%p) if(k&1) ans=1ll*x*ans%p;
        return ans;
    }
    int main()
    {
        scanf("%d%d",&n,&p);fac[0]=1;
        for(int i=1;i<=n*2+1;i++) f[i]=1;
        for(int i=1;i<=n;i++) fac[i]=1ll*i*fac[i-1]%p;
        inv[n]=qpow(fac[n],p-2);
        for(int i=n;i>=1;i--) inv[i-1]=1ll*i*inv[i]%p;
        dfs(1);
        printf("%d
    ",f[1]);
    }
    /*
    g++ 1.cpp -o 1
    ./1
    20 23
    */
    View Code

      C.集合计数(颓了一半题解) 会做么?不会就好题。是在大小为n的集合中选任意个集合,交集元素为k的方案个数。

      我首先想到了把交集为k个提出来,然后求n-k个选交集为0,即f[n-k][0],可以从2^(2^(n-k))-1个减f[n-k][j]转移过来,递推n^2实现很简单就没打,然后想颓了一眼题解(容斥原理),但是难以理解,想了一天又去找DeepinC大神,发现其实是容斥集合的定义和本题的集合不太一样,容斥的集合是包含一个元素的所有方案组成的集合。。。容斥应该作为一个随想随用的算法,不要心理上耸掉,只要好好搞一个容斥系数就可以了

      

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int mn=1e6+20,mod=1e9+7;
    int f[mn],fac[mn],inv[mn],n,k;
    int qpow(int x,int k)
    {
        int ans=1;
        for(;k;k>>=1,x=1ll*x*x%mod) if(k&1) ans=1ll*ans*x%mod;
        return ans;
    }
    long long C(int n,int m) {return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
    int main()
    {
        scanf("%d%d",&n,&k);fac[0]=1;
        for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
        inv[n]=qpow(fac[n],mod-2);
        for(int i=n;i;i--) inv[i-1]=1ll*inv[i]*i%mod;
        long long ans=0,tmp=C(n,k),tp=1;n-=k;
        int op=(n&1)?-1:1;
        for(int i=n;~i;i--)
        {
            ans=(ans+op*C(n,i)*tp%mod)%mod;
            tp=(tp*tp%mod+2*tp%mod)%mod;
            op=-op;
        }
        ans=ans*tmp%mod;
        printf("%lld
    ",(ans+mod)%mod);
    }
    //for sigma i=0->n C(n,i)(2^(2^(n−i))−1)
    /*
    g++ 1.cpp -o 1
    ./1
    4 1
    */
    View Code

     

      D.DZY loves math 2(全颓题解) 劳资他娘的颓了题解还不会,只能等我变强之后在回过头再做。。。


      E.虔诚的母猪人墓主人 (暴力水过,正解算是颓了题解10个字) 这是个数据结构题,思维量很小,就想到一个离散化不影响答案,

      然后每一行有一段段的横区间,区间内左右组合数一定,要维护竖着的ΣC上*C下就行了,没sort调了3节课骂了无数的街。。

      

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #define ll long long
    using namespace std;
    const unsigned int mod=2147483647;
    struct point
    {
        int x,y;
        bool friend operator <(const point &a,const point &b)
        {
            if(a.x==b.x) return a.y<b.y;
            return a.x<b.x;
        }
    }t[100020];
    unsigned int x[100020],y[100020],lx[100020],ly[100020],n,m,nx,ny,w;
    unsigned int b[100020],s[100020],pre[100020];
    unsigned int a[100020],C[100020][15];
    vector<int> r[100020];
    int rd()
    {
        int s=0,w=1;
        char cc=getchar();
        while(cc<'0'||cc>'9') {if(cc=='-') w=-1;cc=getchar();}
        while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar();
        return s*w;
    }
    void add(int i,int w) {for(;i<=ny;i+=(i&(-i))) a[i]+=w;}
    unsigned int ask(int i)
    {
        unsigned int ans=0;
        for(;i;i-=(i&(-i))) ans+=a[i];
        return ans;
    }
    bool cmp(const int &a,const int &b)
    {
        return t[a].y<=t[b].y;
    }
    int main()
    {
        //freopen("25.in","r",stdin);
        n=rd(),m=rd();
        w=rd();
        for(int i=1;i<=w;i++)
        {
            x[i]=lx[i]=rd();
            y[i]=ly[i]=rd();
        }
        int k=rd();
        for(int i=0;i<=w;i++) C[i][0]=1;
        for(int i=1;i<=w;i++) 
            for(int j=1;j<=min(i,k);j++)
            C[i][j]=C[i-1][j]+C[i-1][j-1];
        sort(lx+1,lx+w+1);sort(ly+1,ly+w+1);
        nx=unique(lx+1,lx+w+1)-lx-1,ny=unique(ly+1,ly+w+1)-ly-1;
        for(int i=1;i<=w;i++)
        {
            t[i].x=lower_bound(lx+1,lx+nx+1,x[i])-lx;
            t[i].y=lower_bound(ly+1,ly+ny+1,y[i])-ly;
        }
        sort(t+1,t+w+1);
        for(int i=1;i<=w;i++)
        {
            r[t[i].x].push_back(i);
            pre[i]=s[t[i].y]++;
        }
        unsigned int ans=0;
        for(int i=1;i<=nx;i++)
        {
            //cout<<i<<endl;
            int len=r[i].size();
            sort(r[i].begin(),r[i].end(),cmp);
            if(i!=1)
            {
                for(int j=k;j<=len-k;j++)
                {
                    int ai=t[r[i][j]].y,bi=t[r[i][j-1]].y;
                    if(ai<bi) swap(ai,bi);
                    //if(ai==bi+1) continue;
                    ans+=(ask(ai-1)-ask(bi))*C[j][k]*C[len-j][k];
                    //printf("%d %d %d %d
    ",ask(ai-1)-ask(bi),j,C[j][k],ans);
                }
            }
            for(int j=0;j<len;j++)
            {
                int pr=pre[r[i][j]],nt=s[t[r[i][j]].y]-pr-1,ai=t[r[i][j]].y;
                int wi=C[pr+1][k]*C[nt][k]-ask(ai)+ask(ai-1);
                //printf("%d %d %d %d
    ",j,ask(ai)-ask(ai-1),pr,nt);
                //cout<<r[i][j]<<" "<<pr+1<<" "<<nt<<endl;
                add(ai,wi);
            }
        }
        ans&=mod;
        printf("%d
    ",ans);
    }
    /*
    n%(1<<k)=n&((1<<k)-1)
    g++ 1.cpp -o 1
    ./1
    5 6
    13
    0 2
    0 3
    1 2
    2 0
    1 3
    3 3
    2 1
    2 4
    2 5
    2 6
    3 2
    5 2
    4 3
    2
    */
    听说这是个省选题

      F. 地精部落    (半题解) 他们都用两维数组做得。。。这是个DP题,要找无连续3个数单调,即锯齿状的序列数,问题出在维护信息过多很难P。。然后从状压打起,逐渐减少维护信息,然后再从长度为i,最后一位为k,是峰或谷的状态定义逐渐发现转移错误,后来经tdcp点拨,外加自己思考,发现第二维会转移重复,然后想到其实谁是谁无所谓,只要关心排名是多少就行了,第二维定义为排名为k的方案。

    一般的题解第一句话就是设XXX,转移XXX,然后代码。。。其实最关键的是状态的定义,定义对了转移就显然,状态的定义就要抓住题目的特殊条件,如果维护信息过多就先从状压想起,逐渐减少无用信息,一般就能想到正经状态了

      

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,p,ans=0;
    int f[2][4320][2],sum[2][4320][2];
    int main()
    {
        scanf("%d%d",&n,&p);
        f[2&1][2][1]=f[2&1][1][0]=1;
        sum[2&1][2][1]=sum[2&1][1][0]=1;sum[2&1][2][0]=1;
        for(int i=3;i<=n;i++)
        {
            for(int j=1;j<=i;j++)
            {
                f[i&1][j][0]=(sum[i&1^1][i-1][1]-sum[i&1^1][j-1][1])%p;
                f[i&1][j][1]=(sum[i&1^1][j-1][0])%p;
                sum[i&1][j][0]=(sum[i&1][j-1][0]+f[i&1][j][0])%p;
                sum[i&1][j][1]=(sum[i&1][j-1][1]+f[i&1][j][1])%p;
                //printf("%d %d %d %d
    ",i,j,f[i][j][0],f[i][j][1]);
            }
        }
        int ans=0;
        for(int j=1;j<=n;j++)
        {
            ans+=f[n&1][j][0]+f[n&1][j][1];
            ans%=p;
        }
        while(ans<0) ans+=p;
        printf("%d
    ",ans);
    }
    /*
    g++ 1.cpp -o 1
    ./1
    10 101520127
    */
    View Code

      G.看电影(打表+一节物理自习) 这令我无颜说是自主研发的。。。。看这题样例给的这么紧,先打打表。。。然后狗出了规律。。

      目前还没搞懂到底是为什么,只能再等我变强之后再回过头来去看他。。。。

      总之组合数学是个坑,做了几道简单题发现令人绝望。。。。

      方法:打表找规律,不会就DP,难P就暴力,不会就种地。。。。

    Zeit und Raum trennen dich und mich.时空将你我分开。
  • 相关阅读:
    如何测试复杂的逻辑
    Docker 安装nginx和tomcat
    提高英语
    2020年终总结
    在互联网上班是什么感觉?
    已经过去2周了,你感觉怎么样?
    如何使用玩弄 macOS 的「聚焦搜索」
    如何使用玩弄 macOS 的「聚焦搜索」
    [sdoi2015]排序(搜索+剪枝优化)
    [sdoi 2010][bzoj 1925]地精部落(神仙dp)
  • 原文地址:https://www.cnblogs.com/starsing/p/11142339.html
Copyright © 2020-2023  润新知