• P4099 [HEOI2013]SAO(树形dp)


    P4099 [HEOI2013]SAO

    我们设$f[u][k]$表示以拓扑序编号为$k$的点$u$,以$u$为根的子树中的元素所组成的序列方案数

    蓝后我们在找一个以$v$为根的子树。

    我们的任务就是在合并这两棵树时维护$f[u][k]$

    合并时,$v$的元素可能全在点$u$的前/后面,也可能都有。

    分类讨论:

    1.当有$p(pin [0,siz[v]])$个元素插入到点$u$(拓扑序)前面时

    我们知道插入后点$u$的拓扑序为$k$

    那么插入前的拓扑序即为$k-p$

    ∴插入前子树$u$对应的状态就是$f[u][k-p]$

    设$i=k-p$

    那么插入的方案数就等价于在$k-1$位置($u$是固定最后的)中选择$i-1$个位置$*f[u][i]$

    $=C(k-1,i-1)*f[u][i]$

    其中$iin [1,min(siz[u],k)]$

    2.当有$p(pin [0,siz[v]])$个元素插入到点$u$(拓扑序)后面时

    可以这样表示$p=siz[v]-(k-i)=siz[v]-k+i$

    而原来$u$(拓扑序)后面的元素共有$siz[u]-i$个

    ∴方案数$=C(siz[u]-i+p,p)*f[v][j]=C(siz[u]+siz[v]-k,siz[u]-i)*f[v][j]$

    $j$的范围需要分类:

    1. $u<v$(拓扑序)$jin[k-i+1,siz[v]]$,即点$v$不能填充到拓扑序$<u$的地方
    2. $u>v$(拓扑序)$jin[1,k-i]$

    整理一下:

    $f[u][k]=sum_{i=1}^{min(siz[u],k)}sum_{j}(分类讨论)*C(k-1,i-1)*C(siz[u]+siz[v]-k,siz[u]-i)$

    然鹅这是$O(n^{3})$

    发现是$j$是连续一段区间,可以前缀和处理

    蓝后就愉快地变成$O(n^{2})$了

    end.

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #define re register
    using namespace std;
    typedef long long ll;
    void read(int &x){
        char c=getchar(); x=0;
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    }int wt[50];
    void output(ll x){
        if(!x) {putchar(48); return;}
        int l=0;
        while(x) wt[++l]=x%10,x/=10;
        while(l) putchar(wt[l--]+48);
    }
    int min(int &a,int &b) {return a<b?a:b;}
    const int p=1e9+7;
    ll mod(ll a){return a<p?a:a-p;}
    #define N 1001
    int t,n,siz[N];
    ll f[N][N],C[N][N],sum[N][N],ans;
    int cnt,hd[N],nxt[N<<1],ed[N],poi[N<<1],dir[N<<1];
    bool vis[N];
    void adde(int x,int y,int v){
        nxt[ed[x]]=++cnt; hd[x]=hd[x]? hd[x]:cnt;
        ed[x]=cnt; poi[cnt]=y; dir[cnt]=v;
    }
    void clears(){//清空数据
        for(re int i=1;i<=n;++i){
            f[i][1]=1; sum[i][1]=0;
            vis[i]=0; siz[i]=1;
            hd[i]=ed[i]=0;
            nxt[i]=nxt[i+n]=0;
            for(re int j=2;j<=n;++j) f[i][j]=sum[i][j]=0;
        }cnt=ans=0;
    }
    void dfs(int u){
        vis[u]=1;
        for(int z=hd[u];z;z=nxt[z]){
            int v=poi[z];
            if(vis[v]) continue;
            dfs(v);
            if(dir[z]){//u<v(拓扑序)
                for(int k=siz[u]+siz[v];k>=1;--k){
                    ll tmp=0;
                    for(int i=min(siz[u],k);i>=1;--i){
                        int l=k-i+1,r=siz[v]; //下面只要修改l,r即可
                        if(l>r) continue;
                        ll r1=mod(sum[v][r]-sum[v][l-1]+p);
                        ll r2=C[k-1][i-1]*C[siz[u]+siz[v]-k][siz[u]-i]%p;
                        tmp=mod(tmp+f[u][i]*r1%p*r2%p);
                    }f[u][k]=tmp;
                }
            }else{//u>v(拓扑序)
                for(int k=siz[u]+siz[v];k>=1;--k){
                    ll tmp=0;
                    for(int i=min(siz[u],k);i>=1;--i){
                        int l=1,r=k-i;
                        if(l>r) continue;
                        ll r1=mod(sum[v][r]-sum[v][l-1]+p);
                        ll r2=C[k-1][i-1]*C[siz[u]+siz[v]-k][siz[u]-i]%p;
                        tmp=mod(tmp+f[u][i]*r1%p*r2%p);
                    }f[u][k]=tmp;
                }
            }siz[u]+=siz[v];
        }
        for(int i=1;i<=siz[u];++i)//维护前缀和
            sum[u][i]=mod(sum[u][i-1]+f[u][i]);
    }
    int main(){
        read(t); char opt[3]; int q1,q2,q3;
        for(re int i=0;i<N;++i)//组合数预处理
            for(re int j=0;j<=i;++j)
                C[i][j]=(!j||j==i)?1:mod(C[i-1][j]+C[i-1][j-1]);
        while(t--){
            read(n); clears();
            for(re int i=1;i<n;++i){
                read(q1); scanf("%s",opt); read(q2);
                q3=(opt[0]=='<'); ++q1; ++q2;
                adde(q1,q2,q3); adde(q2,q1,q3^1);
            }dfs(1);
            for(re int i=1;i<=n;++i) ans=mod(ans+f[1][i]);
            output(ans); putchar('
    ');
        }return 0;
    }
  • 相关阅读:
    【黑金动力社区】【原创博文集锦】《Verilog HDL那些事儿》导读
    [黑金原创教程][连载]关于《iBoard 电子学堂》的学习及进阶方式
    发布《iBoard 电子学堂》DEMO代码
    Cyclone III中LVDS的设计
    [黑金原创教程][连载][iBoard 电子学堂][第八卷 设计任意波发生器]第三篇 直接数字合成(DDS)原理
    专业级FPGA视频图像处理开发板发布
    [黑金原创教程][连载][iBoard 电子学堂][第〇卷 电子基础]第二篇 电路图与印刷电路板
    [黑金原创教程][连载][iBoard 电子学堂][第八卷 设计任意波发生器]第一篇 iBoard 任意波发生器简介
    [黑金原创教程][连载][iBoard 电子学堂][第〇卷 电子基础]第一篇 认识电子元器件
    ucgui 在《iBoard 电子学堂》上的移植
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/9805213.html
Copyright © 2020-2023  润新知