"CTS2019" krypton gold hand tour

subject

Click here Look at the topic.

analysis

It is not difficult to find that the edge structure given in the title is a tree. The problem requires that the sequence formed by the first appearance of each card is the number of schemes of a topological order of the tree under the restriction of directed edges.

First of all, for such topics, a classic conclusion is:

The probability of \ (W_i \) in card \ (I \) is drawn. Then, for \ (S\subseteq U,S\not=\varnothing \), the probability that a card \ (x\in S \) is the first drawn out of \ (S \) is:

\[\sum_{k\ge 0}\left(\frac{\sum_{z\in U}W_z-\sum_{y\in S}W_y}{\sum_{z\in U}W_z}\right)^k\cdot \frac{W_x}{\sum_{z\in U}W_z}=\frac{W_x}{\sum_{y\in S}W_y} \]

This conclusion is common in both forward and reverse use.

A wrong way of thinking for a long time:

Note that the final answer is only related to \ (W \) and a topological order \ (\ sigma \), we can write the answer directly:

\[\sum_\sigma\sum_W\left(\prod_{k=1}^np_{\sigma_k,W_k}\prod_{k=1}^nW_k\prod_{k=1}^n\left(\sum_{j=k}^nW_j\right)^{-1}\right) \]

Then, the common factor can be extracted. The outer layer needs enumeration \ (W \), and the inner layer only needs enumeration \ (\ Sigma \). At this time, you can start DP. You only need to be able to successfully maintain \ (\ sum \ Sigma \ prod_k p_{\ sigma_k, W_k} \), but this thing is not simple at all.

After a little study of this wrong direction, we find that the biggest error is that there is no connection tree structure, and only the abstract topological order \ (\ sigma \) is considered. Now we should look back and re-examine the "tree".

Let'S start with a simple case. Can we complete the problem of "tree is an outgoing tree"? At this time, it is required that the node \ (U \) must be obtained before all its sons. Apply the following conclusion. If \ (\ sum W \) including \ (U \) in the \ (U \) subtree is \ (S \), the probability at this time is \ (\ frac{w_}{S} \). Now we only need to maintain \ (dp_{u,s} \) simply to represent the sum of the probabilities of \ (S=s \) in the subtree of \ (U \).

Then, the difference between a general tree and the above situation is that a general tree can have reverse edges. However, if we ignore the restriction of the reverse edge, the original tree is divided into several independent connected blocks, which are calculated and multiplied respectively; At this time, most of the reverse edge restrictions will not be met. We can decide that some properties of the reverse edge are broken, so the reverse edge becomes a positive edge... It is not difficult to find that this algorithm describes a process of tolerance and exclusion. Of course, we don't need to enumerate some reverse edge clouds. When we encounter the reverse edge, we can modify the transfer equation of DP and calculate the content and exclusion.

Summary:

  1. Make full use of known structures. If we ignore the tree structure, the topological order itself will become very obscure; Of course, we should also learn to turn around 😅.
    However, when the answer formula is easy to write, it is an effective method to analyze the formula itself, but it is difficult to deal with the problem after \ (\ sigma \) is separated from the tree.
  2. Consider simple subproblems. First try the unique case of \ (\ sigma \), and then try the case of outgoing tree. It's best not to ignore some special situations. The span is too large and the thinking is difficult.
  3. Pay attention to the mentioned probability conclusion and the method of reverse edge exclusion.

code

#include <cstdio>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int mod = 998244353; 
const int MAXN = 1e3 + 5;

template<typename _T>
void read( _T &x ) {
    x = 0; char s = getchar(); bool f = false;
    while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
    while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    if( f ) x = -x; 
}

template<typename _T>
void write( _T x ) {
    if( x < 0 ) putchar( '-' ), x = -x;
    if( 9 < x ) write( x / 10 );
    putchar( x % 10 + '0' ); 
}

struct Edge {
    int to, nxt;
}Graph[MAXN << 1];

int tmp[MAXN * 3], inv[MAXN * 3];
int dp[MAXN][MAXN * 3];
int siz[MAXN];

int possi[MAXN][4];
int head[MAXN], cnt = 1;

int N;

inline int Qkpow( int, int );
inline int Mul( int x, int v ) { return 1ll * x * v % mod; }
inline int Inv( const int a ) { return Qkpow( a, mod - 2 ); }
inline int Sub( int x, int v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }

inline int Qkpow( int base, int indx ) {
    int ret = 1;
    while( indx ) {
        if( indx & 1 ) ret = Mul( ret, base );
        base = Mul( base, base ), indx >>= 1;
    }
    return ret;
}

inline void AddEdge( const int from, const int to ) {
    Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    head[from] = cnt;
}

void Init( const int n ) {
    inv[1] = 1;
    rep( i, 2, n ) inv[i] = Mul( inv[mod % i], mod - mod / i );
}

void DFS( const int u, const int fa ) {
    siz[u] = 1;
    rep( j, 1, 3 ) dp[u][j] = possi[u][j];
    for( int i = head[u], v ; i ; i = Graph[i].nxt )
        if( ( v = Graph[i].to ) ^ fa ) {
            DFS( v, u ); int s = siz[u] + siz[v];
            if( ! ( i & 1 ) ) {
                rep( j, 1, s * 3 ) tmp[j] = 0;
                rep( j, 1, siz[u] * 3 )
                    rep( k, 1, siz[v] * 3 )
                        tmp[j + k] = Add( tmp[j + k], Mul( Mul( dp[u][j], dp[v][k] ), Mul( j, inv[j + k] ) ) );
                rep( j, 1, s * 3 ) dp[u][j] = tmp[j];
            } else {
                rep( j, 1, s * 3 ) tmp[j] = 0;
                rep( j, 1, siz[u] * 3 )
                    rep( k, 1, siz[v] * 3 ) {
                        tmp[j + k] = Sub( tmp[j + k], Mul( Mul( dp[u][j], dp[v][k] ), Mul( j, inv[j + k] ) ) );
                        tmp[j] = Add( tmp[j], Mul( dp[u][j], dp[v][k] ) );
                    }
                rep( j, 1, s * 3 ) dp[u][j] = tmp[j];
            }
            siz[u] += siz[v];
        }
}

int main() {
    read( N );
    rep( i, 1, N ) {
        int su = 0;
        rep( j, 1, 3 )
            read( possi[i][j] ),
            su = Add( su, possi[i][j] );
        rep( j, 1, 3 )
            possi[i][j] = Mul( possi[i][j], Inv( su ) );
    }
    rep( i, 1, N - 1 ) {
        int u, v;
        read( u ), read( v );
        AddEdge( u, v ), AddEdge( v, u );
    }
    Init( N * 3 );
    DFS( 1, 0 );
    int ans = 0;
    rep( j, 1, 3 * N )
        ans = Add( ans, dp[1][j] );
    write( ans ), putchar( '\n' );
    return 0;
}

Posted on Sat, 04 Dec 2021 21:53:12 -0500 by ponsho