問題ツリーにWC2019 T1番号ソリューション

質問は、表面
タイトルの意味:このタイトルは3タスクが含まれています。

  1. Task0:与えられた2つのツリーのエッジが求めて、S、Tを設定\(BAS ^ {N- | S \ bigcap Tを|} \)
  2. タスク1:求めて、エッジツリーの集合Sが与えられた\(\ sum_ {T}浅 ^ {N- | S \ bigcap Tを|} \)
  3. タスク2:求\(\ sum_ {S} \ sum_ {T} BAS ^ {N- | T bigcap S \ |} \)

前記n個の点の数であり、\(N、BAS \)は、与えられた\(N \ ^ 5のLeq 10 \) に対する答え\(998244353 \)モジュロ。


Task0

直接シミュレーションをすることができます。

#include<bits/stdc++.h>
using namespace std;
#define N 200007
#define ll long long
const ll mod=998244353;
struct str
{
    int x,y;
};
bool operator <(str a,str b)
{
    return a.x<b.x||a.x==b.x&&a.y<b.y;
}
set<str> S;
ll p2(ll x){return x*x%mod;}
ll pw(ll x,ll p)
{
    return p?p2(pw(x,p/2))*(p&1?x:1)%mod:1;
}
int main()
{
    int x,y,n,op;
    ll p;
    scanf("%d%lld%d",&n,&p,&op);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        if(x>y)swap(x,y);
        S.insert({x,y});
    }
    int cnt=0;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        if(x>y)swap(x,y);
        if(S.find({x,y})!=S.end())cnt++;
    }
    ll ans=pw(p,n-cnt);
    printf("%lld\n",ans);
    return 0;
}

タスク1

必要(ANS = \ sum_ {T \ \ {| | S \ bigcap T N-}} BAS ^)

我々は、セット' - (\ {| | S \ bigcap T} = \ sum_ BAS {T} ^ ANS)\次いで、(ANS = BAS ^ N * \ \ ANS)'

その後、我々が対処する方法を検討(| T bigcap S \ | \)\、それは最初の列挙、列挙して、SおよびT.考えることができます

我々は計算する必要があるので\(F(E)は\) 正確にEの交差点を表す\(<S、T> \ ) 番号を。

しかし、考えられ、これらの悪い事は、我々はその後、セットアップ\(G(E)は\) それは少なくともEの交差点を表す\(<S、T> \ ) 番号を。

インクルージョンと我々が持っている排他の原則に従ったので:

\ [F(E)= \ sum_ {E \ subseteq V}( - 1)^ {| V | - | E |} G(V)\]

その後、我々は持っています

\ [回答= \ sum_ {E \ subseteq S} F(E)BAS ^ { - | E |} \]

\ [= \ Sum_ {E \ subseteq S} \ sum_ {E \ subseteq V \ subseteq S}( - 1)^ {| V | - | E |} G(V)BAS ^ { - | E |} \]

\ [= \ Sum_ {V \ subseteq S}( - 1)^ {| V |} G(V)\ sum_ {E \ subseteq V}( - BAS)^ { - | E |} \]

\ [= \ Sum_ {V \ subseteq S}( - 1)^ {| V |} G(V)\ sum_ ^ {iが0 = {} | V |} C_ {| V |} ^ I(-basを^ {-1})^ {I} \]

\ [= \ Sum_ {V \ subseteq S}( - 1)^ {| V |} G(V)、(1-BAS ^ { - 1})^ {| V |} \]

我々は設定\(P =(BAS ^ { - } -1 1)\。) 次いで\(ANS' = \ sum_ { V \ subseteq S} G(V)P ^ {| V |} \)

その後、我々は考える(G(V)\)\カウントする方法

含む少なくとも\(V \) 最初のように見ることができる(V \)\全て形成し、接続の両側\(N - | \ | V)をリンク通信方式番号にブロック、これらのブロックを相互接続。

我々は設定\(V \)通信ブロックサイズが後側に形成され接続されている\({| V |} A_1、A_2 ...、A_ N-を\) 我々は結論付けています。

\ [G(V)= N ^ {N- | V | -2} \ PROD _ {i = 1} ^ {N- | V |} a_iを\]

証明:

私たちはどのように多くのプログラムの木、それに対応するエッジに接続されたすべての通信私たちのブロックを検討してください。

接続側通信プログラムブロックiとブロックjは通信番号\(a_iを* a_j \)

その場合でも、ツリー全体のうち解の数は、\(\ prod_ 1} ^ {I = {N- | V |} ^ {a_iをdeg_i} \) \(deg_iが\)ツリーブロックIと連通しています程度に。

我々はことを見つけるために驚いた\(deg_i \)が Iプラス1のツリーが表示さに対応するpurferシーケンスの数であります

その後、我々はすべてのpurferシーケンスを列挙し、Pとして出現数Pにおけるデジタルiがそれを設定し、\(times_iを\)

\ [G(V)= \ sum_ {P} \ prod_ {I = 1} ^ {N- | V |} a_iを^ {times_i + 1} \]

\ [=(\ prod_ {I = 1} ^ {N- | V |} a_iを)\ sum_ {P} \ prod_ {I = 1} ^ {N- | V |} a_iを^ {times_i} \]

セクションに、それは同じであるの下に\((A_1 + ... + A_ A_2 N- {| V |})^ {N- | V | -2} \) すなわち\(N ^ {N- | V | -2} \)

だから、

\ [G(V)= N ^ {N- | V | -2} \ PROD _ {i = 1} ^ {N- | V |} a_iを\]

QED。

私たちの質問に戻る、そこに

\ [回答= \ sum_ {V \ subseteq S} N ^ {N- | V | -2} P ^ {| V |} \ PRODは_ {I = 1} ^ {N- | V |} a_iを\]

以降の操作の便宜のために、我々\(N、P \)内部にさえによって

\ [ANS' = N ^ { - 2} P ^ {N} \ sum_ {V \ subseteq S} \ PROD _ {i = 1} ^ {N- | V |} a_inP ^ { - 1} \]

その後、我々は設定\(K = nPの^ { - } 1、ANS '' = \ sum_ {V \ subseteq} S \ _のProd 1 = {I} ^ {N- | V |}。a_iK \) 次いで\(ANS '= ANS'」* N ^ { - 2} P ^ N \)

我々は考える(\「ANS」)\カウントする方法

DPが設けられ、容易に木で発生解決\(DP [V] [I] \)サブツリーV内にある、V通信ブロックサイズが全てのIの寄与の寄与に接続され、含まない方式(注通信ブロックVに接続されています)

また、当社は、指定された\(DP [V] [0 ] \) Vサブツリーの答えです。

そして、伝達方程式があります

\ [DP [V] [I] = \ sum_ {J = 1} ^ IDP [V] [J] * DP [U] [IJ] \]

\ [DP [V] [0] = \ sum_ ^ NDP [V] {iは1 =} [I] * I * K \]

初期状態である\(DP [V] [1 ] = 1 \)

最終的な答えがある\(DP [1] [1 ] \)

しかし、この複雑さはある(O(N ^ 2)\ \) 、そしてどのように最適化することを検討します

実際には、我々はすべてのする必要はありません\(DP [V] [I ] \)が算出され、我々は唯一の缶の全体的な価値を知っている必要があります。

我々は設定よう\(F [V] = \ sum_ {I = 0} ^ {N-} DP [V] [I] \)、\ (G [V] = DP [V] [0] = \ sum_ {Iを= 1} ^ NDP [V] [I] * iが\ Kを*)

そして、転送プロセスであり

\ [G [V] = \ sum_ [^ N \ sum_ {J = 1} ^ IDP [V] [J] * DP [U] {iは1 =} IJ] * I * K \]

\ [= \ sum_ {I = 1} ^ N \ sum_ {J = 0} ^ N(I + J)K * DP [V] [I] * DP [U] [J] \]

\ [= \ sum_ {i = 1} ^ N \ sum_ {J = 0} ^ NIK * DP [V] [I] * DP [U] [J] + \ sum_ ^ N \ sum_ {{iは1 =} J = 0} ^ NJK * DP [V] [i]は* DP [U] [J] \]

\ [= G [V] * F [U] + G [U] * F [V] \]

\ [fは[V] = \ sum_ N \ sum_ {J = 1} ^ IDP [V] [J] * DP [U] [IJ] \ ^ {I 1 =}]

\ [=(\ sum_ {i = 1} ^ {n}はDP [V] [i])と(\ sum_ {J = 0} ^ {n}はDP [U] [J])\]

\ [= F [V] * F [U] \]

最後に、\(F [V] + = G [V] \)

初期状態である\(F [V] = 1 、G [V] = K \)

これが可能(O(N)\)\移し

最終的な答え\(BAS ^ NN ^ { - 2} P ^ {N} G [1] \)

#include<bits/stdc++.h>
using namespace std;
#define N 200007
#define M 400007
#define ll long long
const ll mod=998244353;
int f[N],g[N],n,P,K,sz[N];
int hd[N],pre[M],to[M],num;
void adde(int x,int y)
{
    num++;pre[num]=hd[x];hd[x]=num;to[num]=y;
}
void dfs(int v,int fa)
{
    f[v]=1,g[v]=K;
    for(int i=hd[v];i;i=pre[i])
    {
        int u=to[i];
        if(u==fa)continue;
        dfs(u,v);
        g[v]=(1ll*g[v]*f[u]+1ll*g[u]*f[v])%mod;
        f[v]=1ll*f[v]*f[u]%mod;
    }
    f[v]=(f[v]+g[v])%mod;
}
ll p2(ll x){return x*x%mod;}
ll pw(ll x,ll p)
{
    return p?p2(pw(x,p/2))*(p&1?x:1)%mod:1;
}
int main()
{
    //freopen("data.in","r",stdin);
    int x,y,op,p;
    scanf("%d%d%d",&n,&p,&op);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        adde(x,y),adde(y,x);
    }
    if(p==1)
    {
        printf("%lld\n",pw(n,n-2));
        return 0;
    }
    P=pw(pw(p,mod-2)-1,mod-2),K=1ll*n*P%mod;
    dfs(1,0);
    ll ans = g[1] * p2(pw(n,mod-2)) % mod * pw(pw(P,mod-2),n) % mod;
    printf("%lld\n",ans*pw(p,n)%mod);
    return 0;
}

Task2の

同様にタスク1、私たちも得ることができます

\ [回答= \ sum_ {V} G(V)P ^ {| V |} \]

前記\(G(V)=(N ^ {N- | V | -2} \ {I = prod_ 1} ^ {N- | V |} a_iを)^ 2 \)、Vは任意の点nが森林の辺の集合

簡素化にゲット

\ [ANS' = N ^ { - 4} P ^ N \ sum_ {V} \ prod_ {I = 1} ^ {N- | V |}(a_iを^ 2N ^ 2P ^ { - 1})\]

\(K = N ^ 2P ^ { - 1}、ANS '' = \ sum_ {V} \ prod_ {I = 1} ^ {N- | V |}(a_iを^ 2K)\)

\(ANS' = N ^ { - 4} P ^のNaN「\)

検討\(ANS「」\) 各分割方式の寄与は、ツリーを構成する各ブロック内の通信、ブロック通信を分けた対応する数にn個の点、\(\ prod_ {iは1 = } N- {^ | V | a_iを}} ^ {a_iを-2(a_iを^ 2K)\) 通信寄与の各ブロックのサイズはIである、請求\(I ^ {I-2 }(I ^ 2K)= I ^ IK \)

よると生成機能のいくつかのプロパティ我々が許可すれば、\(G(X)= \ sum_ {私は= 1} ^ \ inftyの\ FRAC {I ^ IK} {I!} X ^ I \) その後、\(E ^ { G(x)は} \)であり、'\)\(ANS'初撮影指数生成機能、\(X ^ N \)を乗じたアイテムを\(N!\)を得ることができる\(ANS '' \) 。

多項式\(EXP \)をすることができます。

最終的な答え\(BAS ^ NN ^ { - 4} P ^のNaN '' \)

(完全な文特殊コードに注意\(BAS = 1 \)の場合):

#include<bits/stdc++.h>
using namespace std;
#define N 600007
#define ll long long
const ll mod=998244353;
const int lim=2e5;
int tp;
ll n,bas;
ll p2(ll x){return x*x%mod;}
ll pw(ll x,ll p)
{
    return p?p2(pw(x,p/2))*(p&1?x:1)%mod:1;
}
namespace tp0
{
    struct edge
    {
        int x,y;
    };
    bool operator <(edge a,edge b)
    {
        return a.x<b.x||a.x==b.x&&a.y<b.y;
    }
    set<edge> S;
    void work()
    {
        if(bas==1)
        {
            printf("%d\n",1);
            return ;
        }
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(x>y)swap(x,y);
            S.insert({x,y});
        }
        int cnt=0;
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(x>y)swap(x,y);
            if(S.find({x,y})!=S.end())cnt++;
        }
        printf("%lld\n",pw(bas,n-cnt));
    }
}
namespace tp1
{
    int hd[N],pre[N],to[N],num;
    ll f[N],g[N],K,P,Q;
    void adde(int x,int y)
    {
        num++;pre[num]=hd[x];hd[x]=num;to[num]=y;
    }
    void dfs(int v,int fa)
    {
        f[v]=1,g[v]=K;
        for(int i=hd[v];i;i=pre[i])
        {
            int u=to[i];
            if(u==fa)continue;
            dfs(u,v);
            g[v]=(g[v]*f[u]+f[v]*g[u])%mod;
            f[v]=f[v]*f[u]%mod;
        }
        f[v]=(f[v]+g[v])%mod;
    }
    void work()
    {
        if(bas==1)
        {
            printf("%lld\n",pw(n,n-2));
            return ;
        }
        P=(pw(bas,mod-2)-1+mod)%mod;
        K=n*pw(P,mod-2)%mod;
        Q=pw(n,2*(mod-2))*pw(P,n)%mod;
        int x,y;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            adde(x,y),adde(y,x);
        }
        dfs(1,0);
        ll ans=g[1];
        ans=ans*Q%mod;
        ans=ans*pw(bas,n)%mod;
        printf("%lld\n",ans);
    }
}
namespace tp2
{
    ll inv[N],fac[N],ifac[N];
    int rev[N],len;
    void getlen(int n)
    {
        for(len=1;len<=n;len<<=1);
        for(int i=0;i<len;i++)
            rev[i]=rev[i>>1]>>1|(i&1?len>>1:0);
    }
    void NTT(ll *a,int op)
    {
        for(int i=0;i<len;i++)
            if(rev[i]<i)swap(a[rev[i]],a[i]);
        for(int i=1;i<len;i<<=1)
        {
            ll nw=pw(3,(mod-1)/(i<<1));
            for(int j=0;j<len;j+=i<<1)
            {
                ll w=1;
                for(int k=j;k<j+i;k++)
                {
                    ll x=a[k],y=a[k+i]*w%mod;
                    a[k]=(x+y)%mod,a[k+i]=(x-y+mod)%mod;
                    w=w*nw%mod;
                }
            }
        }
        if(op<0)
        {
            reverse(a+1,a+len);
            ll Inv=pw(len,mod-2);
            for(int i=0;i<len;i++)
                a[i]=a[i]*Inv%mod;
        }
    }
    void copy(ll *a,ll *b,int n=len)
    {
        for(int i=0;i<n;i++)a[i]=b[i];
        for(int i=n;i<len;i++)a[i]=0;
    }
    ll mul_c[N],mul_d[N];
    void mul(ll *t,ll *a,ll *b)
    {
        ll *c=mul_c,*d=mul_d;
        copy(c,a),copy(d,b);
        NTT(c,1),NTT(d,1);
        for(int i=0;i<len;i++)c[i]=c[i]*d[i]%mod;
        NTT(c,-1);
        copy(t,c);
    }
    ll inv_c[N];
    void getinv(int p,ll *a,ll *b)
    {
        if(p==1)return a[0]=pw(b[0],mod-2),(void)1;
        getinv((p+1)/2,a,b);
        getlen(2*p);
        ll *c=inv_c;
        copy(c,b,p);
        NTT(a,1),NTT(c,1);
        for(int i=0;i<len;i++)a[i]=a[i]*(2-a[i]*c[i]%mod+mod)%mod;
        NTT(a,-1);
        for(int i=p;i<len;i++)a[i]=0;
    }
    void devir(ll *a,int n)
    {
        for(int i=1;i<=n;i++)a[i-1]=a[i]*i%mod;
        a[n]=0;
    }
    void inter(ll *a,int n)
    {
        for(int i=n;i>=0;i--)a[i+1]=a[i]*inv[i+1]%mod;
        a[0]=0;
    }
    ll ln_c[N];
    void getln(int n,ll *a,ll *b)
    {
        ll *c=ln_c;
        getlen(2*n);
        copy(c,b,n);
        getinv(n,a,c);
        devir(c,n);
        getlen(2*n);
        mul(a,a,c);
        inter(a,n);
        for(int i=n;i<len;i++)a[i]=0;
    }
    ll exp_c[N];
    void getexp(int p,ll *a,ll *b)
    {
        if(p==1)return a[0]=1,(void)1;
        getexp((p+1)/2,a,b);
        getlen(2*p);
        ll *c=exp_c;
        copy(c,a,0);
        getln(p,c,a);
        getlen(2*p);
        for(int i=0;i<p;i++)c[i]=(b[i]-c[i]+mod)%mod;
        c[0]=(c[0]+1)%mod;
        mul(a,a,c);
        for(int i=p;i<len;i++)a[i]=0;
    }
    void Init()
    {
        fac[0]=1;
        for(int i=1;i<=lim;i++)fac[i]=fac[i-1]*i%mod;
        ifac[lim]=pw(fac[lim],mod-2);
        for(int i=lim;i>=1;i--)ifac[i-1]=ifac[i]*i%mod;
        inv[1]=1;
        for(int i=2;i<=lim;i++)
            inv[i]=mod-mod/i*inv[mod%i]%mod;
    }
    ll f[N],g[N];
    void work()
    {
        Init();
        ll P,K;
        if(bas==1)
        {
            printf("%lld\n",pw(n,2*(n-2)));
            return ;
        }
        P=(pw(bas,mod-2)-1+mod)%mod;
        K=n*n%mod*pw(P,mod-2)%mod;
        for(int i=1;i<=n;i++)
            f[i]=pw(i,i)*K%mod*ifac[i]%mod;
        getexp(n+1,g,f);
        ll ans=g[n]*fac[n]%mod;
        ans=ans*pw(n,4*(mod-2))%mod*pw(P,n)%mod;
        ans=ans*pw(bas,n)%mod;
        printf("%lld\n",ans);
    }
}

int main()
{
    scanf("%lld%lld%d",&n,&bas,&tp);
    if(tp==0)tp0::work();
    else if(tp==1)tp1::work();
    else tp2::work();
    return 0;
}

おすすめ

転載: www.cnblogs.com/lishuyu2003/p/12146391.html