洛谷P3240 [HNOI2015]實驗比較

鏈接

點擊跳轉

題解

dpi,jdp_{i,j}表示以ii爲根的子樹,分成jj段的方案數

狀態解釋:jj段,每一段內部都是等號連接的,段與段之間是小於號連接的

做樹形dpdp,合併的時候,假設用dp[u][]dp[u][\dots]dp[v][]dp[v][\dots]去合併得到dp[w][]dp[w][\dots]

dp[u][i]×dp[v][j]dp[u][i]\times dp[v][j]dp[w][k]dp[w][k]的貢獻分兩步計算:

先從uu中選擇ii段出來,再從vv中選jj段出來,然後再把這ii段和jj段合併起來,形成kk

合併的時候,必須是來自不同子樹的纔可以合併成同一塊

假設把ii個來自第一棵子樹的塊和jj個來自第二課子樹的塊合併成kk個塊,第一塊必須是來自第一棵子樹中的第一塊,而且每個塊不能爲空,每個塊不能包含來自同一棵子樹的兩塊。滿足上述條件的方案數記作gijkg_{ijk}

那麼這個dpdp寫出來就是

dpw,k=i,jdpu,i×dpv,j×gijk dp_{w,k} = \sum_{i,j} dp_{u,i} \times dp_{v,j} \times g_{ijk}

任何兩個點只在lcalca處進行一次O(n)O(n)(樹爲一條鏈的時候取到O(n)O(n),否則更小)的合併,所以最終複雜度是O(n3)O(n^3)

代碼

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define eps 1e-8
#define maxn 110
#define maxe 210
#define cl(x) memset(x,0,sizeof(x))
#define rep(i,a,b) for(i=a;i<=b;i++)
#define em(x) emplace(x)
#define emb(x) emplace_back(x)
#define emf(x) emplace_front(x)
#define fi first
#define se second
#define de(x) cerr<<#x<<" = "<<x<<endl
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll read(ll x=0)
{
    ll c, f(1);
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-0x30;
    return f*x;
}
struct Graph
{
    int etot, head[maxn], to[maxe], next[maxe], w[maxe];
    void clear(int N)
    {
        for(int i=1;i<=N;i++)head[i]=0;
        etot=0;
    }
    void adde(int a, int b, int c=0){to[++etot]=b;w[etot]=c;next[etot]=head[a];head[a]=etot;}
    #define forp(pos,G) for(auto p=G.head[pos];p;p=G.next[p])
}G;
struct UnionFind
{
	ll f[maxn], size[maxn];
	void init(ll n)
	{
		for(auto i=1;i<=n;i++)f[i]=i, size[i]=1;
	}
	ll find(ll x){return f[x]==x?x:f[x]=find(f[x]);}
	void merge(ll x, ll y)
    {
        auto fx=find(x), fy=find(y);
        f[fx]=fy;
        if(fx!=fy)size[fy]+=size[fx];
    }
}uf, uf2;
#define mod 1'000'000'007
ll n, dp[maxn][maxn], fa[maxn], indeg[maxn], sz[maxn], m, tmp[maxn], f[maxn][maxn][maxn];
vector< tuple<ll,char,ll> > lis;
void merge(ll u, ll v)
{
    ll i, j, k, mx=sz[u]+sz[v];
    rep(k,1,mx)tmp[k]=0;
    rep(k,1,mx)
    {
        rep(i,1,sz[u])rep(j,1,sz[v])(tmp[k]+=dp[u][i]*dp[v][j]%mod*f[i][j][k])%=mod;
    }
    rep(k,1,mx)dp[u][k]=tmp[k];
}
void dfs(ll pos)
{
    dp[pos][1]=1;
    sz[pos]=1;
    forp(pos,G)
    {
        auto to=G.to[p];
        dfs(to);
        merge(pos,to);
        sz[pos]+=sz[to];
    }
}
int main()
{
    ll i, j, k;
    n=read(), m=read();
    uf.init(n);
    uf2.init(n+1);
    rep(i,1,m)
    {
        ll f, u; char s[maxn];
        scanf("%lld%s%lld",&f,s,&u);
        lis.emb( make_tuple(f,*s,u) );
    }
    for(auto tp:lis)
    {
        ll f=get<0>(tp), c=get<1>(tp), u=get<2>(tp);
        if(c=='=')uf.merge(u,f);
    }
    for(auto tp:lis)
    {
        ll f=get<0>(tp), c=get<1>(tp), u=get<2>(tp);
        if(c=='<')
        {
            fa[uf.find(u)]=uf.find(f);
            indeg[uf.find(f)]++;
        }
    }
    rep(i,1,n)if(uf.find(i)==i and fa[i]==0)
    {
        fa[i]=n+1;
        indeg[n+1]++;
    }
    rep(i,1,n)if(uf.find(i)==i)
    {
        G.adde(fa[i],i);
        if(uf2.find(fa[i])!=uf2.find(i))
        {
            uf2.merge(fa[i],i);
        }
        else
        {
            printf("0");
            return 0;
        }
    }
    f[1][0][1]=1;
    for(k=2;k<=n+1;k++)rep(i,0,n+1)rep(j,0,n+1)
    {
        if(i)f[i][j][k]+=f[i-1][j][k-1];
        if(j)f[i][j][k]+=f[i][j-1][k-1];
        if(i and j)f[i][j][k]+=f[i-1][j-1][k-1];
        f[i][j][k]%=mod;
    }
    dfs(n+1);
    ll ans=0;
    rep(i,1,n+1)(ans+=dp[n+1][i])%=mod;
    printf("%lld",ans);
    return 0;
}
發佈了910 篇原創文章 · 獲贊 75 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章