Atcoder TypicalDPContest N~T

https://tdpc.contest.atcoder.jp/assignments

N

简单的树形DP,把加边转化成加点,组合数简单算一下。

CO int N=1e3+10;
int fac[N],ifac[N];
vector<int> to[N];
int siz[N],dp[N];

void dfs(int u,int fa){
    siz[u]=1;
    for(int v:to[u])if(v!=fa){
        dfs(v,u);
        siz[u]+=siz[v];
    }
    dp[u]=fac[siz[u]-1];
    for(int v:to[u])if(v!=fa)
        dp[u]=mul(dp[u],mul(dp[v],ifac[siz[v]]));
}
int main(){
    int n=read<int>();
    fac[0]=1;
    for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
    ifac[n]=fpow(fac[n],mod-2);
    for(int i=n-1;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
    for(int i=1;i<n;++i){
        int u=read<int>(),v=read<int>();
        to[u].push_back(v),to[v].push_back(u);
    }
    int ans=0;
    for(int i=1;i<=n;++i){
        dfs(i,0);
        ans=add(ans,dp[i]);
    }
    printf("%d\n",mul(ans,ifac[2]));
    return 0;
}

O

插空法的运用。

int a[27],s[27];
int fac[261],ifac[261];
int dp[27][261],cnt[11][11];

IN int binom(int n,int m){
    return mul(fac[n],mul(ifac[m],ifac[n-m]));
}
void dfs(int x,int s){
    ++cnt[s][x];
    for(int i=1;i<=10-s;++i) dfs(x+1,s+i);
}
int main(){
    int n=0;
    for(int i=1;i<=26;++i)if(read(a[i])){
        a[++n]=a[i];
        s[n]=s[n-1]+a[n];
    }
    fac[0]=1;
    for(int i=1;i<=260;++i) fac[i]=mul(fac[i-1],i);
    ifac[260]=fpow(fac[260],mod-2);
    for(int i=259;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
    dfs(0,0);
    dp[0][0]=1;
    for(int i=0;i<n;++i)for(int j=0;j<=s[i];++j)if(dp[i][j]){
        for(int k=1;k<=a[i+1];++k)if(k<=s[i]+1){
            for(int x=0;x<=k;++x)if(x<=j and k-x<=s[i]+1-j)
                dp[i+1][j-x+a[i+1]-k]=add(dp[i+1][j-x+a[i+1]-k],
                mul(dp[i][j],mul(cnt[a[i+1]][k],mul(binom(s[i]+1-j,k-x),binom(j,x)))));
        }
    }
    printf("%d\n",dp[n][0]);
    return 0;
}

P

选不相交的链。DP状态同 林克卡特树。

CO int N=1e3+10;
int K;
vector<int> to[N];
int dp[N][51][3],tmp[51][3];

void dfs(int u,int fa){
    dp[u][0][0]=1;
    for(int v:to[u])if(v!=fa){
        dfs(v,u);
        for(int i=0;i<=K;++i) fill(tmp[i],tmp[i]+3,0);
        for(int i=0;i<=K;++i){
            for(int j=0;j<=i;++j){
                tmp[i][0]=add(tmp[i][0],
                mul(dp[u][j][0],add(dp[v][i-j][0],add(dp[v][i-j][1],dp[v][i-j][2])))
                );
                tmp[i][1]=add(tmp[i][1],add(
                mul(dp[u][j][0],add(i-j-1>=0?dp[v][i-j-1][0]:0,dp[v][i-j][1])),
                mul(dp[u][j][1],add(dp[v][i-j][0],add(dp[v][i-j][1],dp[v][i-j][2])))
                ));
                tmp[i][2]=add(tmp[i][2],add(
                mul(dp[u][j][1],add(dp[v][i-j][0],i-j+1<=K?dp[v][i-j+1][1]:0)),
                mul(dp[u][j][2],add(dp[v][i-j][0],add(dp[v][i-j][1],dp[v][i-j][2])))
                ));
            }
        }
        for(int i=0;i<=K;++i) copy(tmp[i],tmp[i]+3,dp[u][i]);
    }
}
int main(){
    int n=read<int>();
    read(K);
    for(int i=1;i<n;++i){
        int u=read<int>(),v=read<int>();
        to[u].push_back(v),to[v].push_back(u);
    }
    dfs(1,0);
    printf("%d\n",add(dp[1][K][0],add(dp[1][K][1],dp[1][K][2])));
    return 0;
}

Q

为了不重复只能加01字符。

为了知道是否成段需要记录结束位置。

但是这样没法转移。可以再存一个AC自动机状态,也可以再存末7位的数。可以发现结束位置需要记录8位。

https://suikaba.hatenablog.com/entry/2017/08/27/181249

mask:

int w[9][1<<8];
int pre[1<<7][1<<8][2];
int dp[101][1<<7][1<<8];

int main(){
    int n=read<int>(),L=read<int>();
    for(int i=1;i<=n;++i){
        static char s[9];scanf("%s",s);
        int len=strlen(s);
        int b=0;
        for(int j=0;j<len;++j) b|=(s[j]-'0')<<j;
        w[len][b]=1;
    }
    for(int j=0;j<1<<7;++j)for(int k=0;k<1<<8;++k)
        for(int l=0;l<2;++l){
            int f=0;
            for(int m=0;m<8;++m)if(k>>m&1){
                int b=(j<<1|l)&((1<<(m+1))-1);
                if(w[m+1][b]) {f=1;break;}
            }
            pre[j][k][l]=f;
        }
    dp[0][0][1]=1;
    for(int i=0;i<L;++i)for(int j=0;j<1<<7;++j)
        for(int k=0;k<1<<8;++k)if(dp[i][j][k])
            for(int l=0;l<2;++l){
                int f=pre[j][k][l];
                int nj=(j<<1&127)|l;
                int nk=(k<<1&255)|f;
                dp[i+1][nj][nk]=add(dp[i+1][nj][nk],dp[i][j][k]);
            }
    int ans=0;
    for(int j=0;j<1<<7;++j)for(int k=0;k<1<<7;++k)
        ans=add(ans,dp[L][j][k<<1|1]);
    printf("%d\n",ans);
    return 0;
}

AC自动机:

int tot,ch[3587][2],fa[3587],val[3587];

void insert(char s[],int n){
    int x=0;
    for(int i=1;i<=n;++i){
        int c=s[i]-'0';
        if(!ch[x][c]) ch[x][c]=++tot;
        x=ch[x][c];
    }
    val[x]|=1<<(n-1);
}

int dp[2][3587][1<<8];

int main(){
    int n=read<int>(),L=read<int>();
    for(int i=1;i<=n;++i){
        static char s[10];scanf("%s",s+1);
        insert(s,strlen(s+1));
    }
    deque<int> Q;
    for(int c=0;c<2;++c)
        if(ch[0][c]) Q.push_back(ch[0][c]);
    while(Q.size()){
        int x=Q.front();Q.pop_front();
        val[x]|=val[fa[x]];
        for(int c=0;c<2;++c){
            if(!ch[x][c]) ch[x][c]=ch[fa[x]][c];
            else{
                fa[ch[x][c]]=ch[fa[x]][c];
                Q.push_back(ch[x][c]);
            }
        }
    }
    dp[0&1][0][1]=1;
    for(int i=0;i<L;++i){
        for(int x=0;x<=tot;++x) fill(dp[(i+1)&1][x],dp[(i+1)&1][x]+(1<<8),0);
        for(int j=0;j<=tot;++j)for(int k=0;k<1<<8;++k)
            if(dp[i&1][j][k])for(int l=0;l<2;++l){
                int nj=ch[j][l];
                bool f=k&val[nj];
                int nk=(k<<1&255)|f;
                dp[(i+1)&1][nj][nk]=add(dp[(i+1)&1][nj][nk],dp[i&1][j][k]);
            }
    }
    int ans=0;
    for(int j=0;j<=tot;++j)for(int k=0;k<1<<7;++k)
        ans=add(ans,dp[L&1][j][k<<1|1]);
    printf("%d\n",ans);
    return 0;
}

R

把走两次看成两个节点同时走即可。

CO int N=310;
namespace G{
    vector<int> to[N];
    int pos[N],low[N],dfn;
    int stk[N],top,ins[N];
    int col[N],scc;
    
    void tarjan(int u){
        pos[u]=low[u]=++dfn;
        stk[++top]=u,ins[u]=1;
        for(int v:to[u]){
            if(!pos[v]){
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(ins[v]) low[u]=min(low[u],pos[v]);
        }
        if(low[u]==pos[u]){
            ++scc;
            do{
                int x=stk[top];
                col[x]=scc,ins[x]=0;
            }while(stk[top--]!=u);
        }
    }
    void main(int n){
        for(int i=1;i<=n;++i)
            if(!pos[i]) tarjan(i);
    }
}

int val[N],eg[N][N];
vector<int> to[N];
int deg[N],idx[N];
int dp[N][N];

int main(){
    int n=read<int>();
    for(int u=1;u<=n;++u)for(int v=1;v<=n;++v)
        if(read<int>()) G::to[u].push_back(v);
    G::main(n);
    for(int u=1;u<=n;++u){
        ++val[G::col[u]];
        for(int v:G::to[u])if(G::col[v]!=G::col[u]){
            if(eg[G::col[u]][G::col[v]]) continue;
            to[G::col[u]].push_back(G::col[v]),++deg[G::col[v]];
            eg[G::col[u]][G::col[v]]=1;
        }
    }
    n=G::scc;
    for(int k=1;k<=n;++k)
        for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)
            eg[i][j]|=eg[i][k]&eg[k][j];
    deque<int> Q;
    for(int i=1;i<=n;++i)
        if(!deg[i]) Q.push_back(i);
    while(Q.size()){
        int u=Q.front();Q.pop_front();
        idx[++idx[0]]=u;
        for(int v:to[u])if(--deg[v]==0)
            Q.push_back(v);
    }
    for(int i=1;i<=n;++i)for(int j=1;j<i;++j){
        int u=idx[i],v=idx[j];
        dp[u][v]=max(dp[u][v],val[u]+val[v]);
        for(int k=i+1;k<=n;++k){
            int w=idx[k];
            if(eg[u][w]) dp[w][v]=max(dp[w][v],dp[u][v]+val[w]);
            if(eg[v][w]) dp[w][u]=max(dp[w][u],dp[u][v]+val[w]);
        }
    }
    int ans=0;
    for(int u=1;u<=n;++u)for(int v=1;v<=n;++v)
        ans=max(ans,dp[u][v]);
    printf("%d\n",ans);
    return 0;
}

S

需要用复杂的状态记录连通性。

https://suikaba.hatenablog.com/entry/2017/08/24/164134

struct DisjointSet{
    vector<int> fa;
    
    DisjointSet(int n):fa(n){
        for(int i=0;i<n;++i) fa[i]=i;
    }
    int find(int x){
        return fa[x]==x?x:fa[x]=find(fa[x]);
    }
    IN void merge(int x,int y){
        fa[find(x)]=find(y);
    }
};

int main(){
    int H=read<int>(),W=read<int>();
    vector<int> pw(8);
    pw[0]=1;
    for(int i=1;i<8;++i) pw[i]=pw[i-1]*7;
    vector<int> cur(pw[H]);
    for(int i=0;i<1<<H;++i)if(i&1){
        int s=1;
        int label=1;
        for(int j=1;j<H;++j){
            if(i>>j&1) s+=label*pw[j];
            else if(i>>(j-1)&1) ++label;
        }
        cur[s]=1;
    }
    for(int i=0;i<W-1;++i){
        vector<int> nxt(pw[H]);
        for(int j=0;j<pw[H];++j)if(cur[j])
            for(int k=0;k<1<<H;++k){
                DisjointSet uf(H);
                vector<int> p(H);
                for(int l=0;l<H;++l)if(k>>l&1){
                    if(j/pw[l]%7==1) p[l]=1;
                    for(int m=l+1;m<H;++m)if(k>>m&1){
                        if(m==l+1) uf.merge(l,m);
                        else{
                            int u=j/pw[l]%7,v=j/pw[m]%7;
                            if(u>0 and u==v) uf.merge(l,m);
                        }
                    }
                }
                for(int l=0;l<H;++l)if(p[l]==1)
                    p[uf.find(l)]=1;
                int nj=0;
                int label=2;
                for(int l=0;l<H;++l)if(k>>l&1){
                    int u=uf.find(l);
                    if(p[u]==0) p[u]=label++;
                    nj+=pw[l]*p[u];
                }
                nxt[nj]=add(nxt[nj],cur[j]);
            }
        cur.swap(nxt);
    }
    int ans=0;
    for(int i=0;i<pw[H];++i)if(i/pw[H-1]%7==1)
        ans=add(ans,cur[i]);
    printf("%d\n",ans);
    return 0;
}

T

齐次线性递推模板题。

poly operator*(CO poly&a,CO poly&b){
    int n=a.size()-1,m=b.size()-1;
    poly ans(n+m+1);
    for(int i=0;i<=n;++i)for(int j=0;j<=m;++j)
        ans[i+j]=add(ans[i+j],mul(a[i],b[j]));
    return ans;
}
poly operator%(poly a,CO poly&b){
    int n=a.size()-1,m=b.size()-1;
    if(n<m) return a;
    for(int i=n;i>=m;--i){
        int coef=mul(mod-a[i],fpow(b[m],mod-2));
        for(int j=i;j>=i-m;--j) a[j]=add(a[j],mul(coef,b[j-(i-m)]));
    }
    return poly(a.begin(),a.begin()+m);
}
int main(){
    int k=read<int>(),n=read<int>();
    if(n<=k){
        puts("1");
        return 0;
    }
    poly a(k+1);
    for(int i=1;i<=k;++i) a[k-i]=1;
    a[k]=mod-1;
    poly f(k+1);
    for(int i=1;i<=k;++i) f[i]=1;
    poly rmd={1},tmp={0,1};
    for(--n;n;n>>=1,tmp=tmp*tmp%a)
        if(n&1) rmd=rmd*tmp%a;
    int ans=0;
    for(int i=0;i<k;++i) ans=add(ans,mul(rmd[i],f[i+1]));
    printf("%d\n",ans);
    return 0;
}
上一篇:[HDU5184] Brackets


下一篇:ARC93F Dark Horse