0x5C 计数类DP

cf 559C 考虑到黑色的格子很少,那么我把(1,1)变成黑色,然后按每个黑色格子接近终点的程度排序,计算黑色格子不经过另一个黑色格子到达终点的方案,对于当前的格子,要减去在它右下角的所有方案数(注意不是f值)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL mod=1e9+7;
LL MOD(LL x){return (x%mod+mod)%mod;}

LL jc[310000],inv[310000];
LL quick_pow(LL A,LL p)
{
    LL ret=1;
    while(p!=0)
    {
        if(p%2==1)ret=(ret*A)%mod;
        A=(A*A)%mod;p/=2;
    }
    return ret;
}
LL getC(int n,int m){return jc[m]*inv[m-n]%mod*inv[n]%mod;}

struct node{int x,y;}a[2100];
bool cmp(node n1,node n2){return n1.x+n1.y>n2.x+n2.y;}
LL f[2100];
int main()
{
    jc[0]=1,inv[0]=1;for(int i=1;i<=300000;i++)jc[i]=(jc[i-1]*i)%mod,inv[i]=quick_pow(jc[i],mod-2);
    int n,m,K;
    scanf("%d%d%d",&n,&m,&K);
    for(int i=1;i<=K;i++)
        scanf("%d%d",&a[i].x,&a[i].y);
    a[++K].x=1,a[K].y=1;
    sort(a+1,a+K+1,cmp);
    
    for(int i=1;i<=K;i++)
    {
        f[i]=getC(n-a[i].x,(n+m)-(a[i].x+a[i].y));
        for(int j=1;j<i;j++)
            if(a[i].x<=a[j].x&&a[i].y<=a[j].y)
            {
                f[i]=MOD( f[i]-MOD(f[j]*getC(a[j].x-a[i].x,(a[j].x+a[j].y)-(a[i].x+a[i].y))) );
            }
    }
    printf("%I64d\n",f[K]);
    return 0;
}
cf 559C

poj1737 口胡一波题解,我们知道n个点的无向图个数有2^(n*(n-1)/2)个,那么就去算不联通的,假设存在有一个包括点1的块大小为k,剩下的就是n-k个点的无向图个数了。

poj1037 其实可以借鉴一下康托展开的思想的。。。对于当前位应该选取最大的那个剩下位方案数少于m的,那么方案数就要用DP维护了。设f[i][j][k]表示枚举到第几位,选的是当前排第j的,是高位还是低位。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
LL f[30][30][2];

void initf()
{
    f[1][1][0]=f[1][1][1]=1;
    for(int i=2;i<=20;i++)
        for(int j=1;j<=i;j++)
        {
            for(int k=j;k<=i-1;k++)f[i][j][0]+=f[i-1][k][1];
            for(int k=1;k<=j-1;k++)f[i][j][1]+=f[i-1][k][0];
        }
}

bool v[30];
int main()
{
    initf();
    
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;LL m;
        scanf("%d%lld",&n,&m);
        memset(v,false,sizeof(v));
        int x,k;
        for(int j=1;j<=n;j++)
        {
            if(f[n][j][1]>=m){x=j,k=1;break;}
            else m-=f[n][j][1];
            
            if(f[n][j][0]>=m){x=j,k=0;break;}
            else m-=f[n][j][0];
        }
        v[x]=true;printf("%d",x);
        for(int i=2;i<=n;i++)
        {
            k^=1; int j=0;
            for(int y=1;y<=n;y++)
            {
                if(v[y]==true)continue;
                j++;
                
                if((k==0&&y<x)||(k==1&&y>x))
                {
                    if(f[n-i+1][j][k]>=m){x=y;break;}
                    else m-=f[n-i+1][j][k];
                }
            }
            v[x]=true;printf(" %d",x);
        }
        printf("\n");
    }
    return 0;
}
poj1037

 

posted @ 2018-08-15 16:25  AKCqhzdy  阅读(308)  评论(0编辑  收藏  举报