初三年后集训测试T3---树上的宝藏

初三年后集训测试 $T 3 $ 树上的宝藏

$$HZOI$$


·题意

· \(Description\)

蒜头君有一棵 \(n\) 个节点的树(即 \(n\) 个节点, \(n−1\) 条边的无向连通图)。树的每个节点上都有一个宝藏。蒜头君准备大动干戈,拿到这些保证。

但是在拿宝藏之前,蒜头君发现了一个问题,由于树的边的材质问题,若两个节点被一条边直接连接,为了确保安全,那么这两个节点上的宝藏最多可以拿一个。

好在同样擅长化学的巨佬--花椰妹给了蒜头君一条特殊材质的边。蒜头君可以选定一条边并将这条边的材质替换成特殊材质的边,于是为了确保安全,被这条选定的边直接相连的两个节点上的宝藏最少拿一个。

蒜头君想知道,对于每一条边,若选定这条边替换成花椰妹送给他的特殊材质的边,在确保安全的情况下,有多少种拿的方法是可行的。

· \(Input\)

第一行一个正整数 \(n\)

后面 \(n−1\) 行,第 \(i\) 行有每行两个正整数 \(u\) , \(v\) 代表第 \(i−1\) 条边连接着点 \(u\) 和点 \(v\)

· \(Output\)

\(n-1\) 行 , 每行一个数,代表此边为特殊边时的方案数,输出的是对 \(998244353\) 取模的余数 。


·题解


·分析

其实换根还是很好想的。

先不考虑特殊边。

先定义一个 \(dp\) 表示在以 \(1\) 为根的情况下,以 \(i\) 为根的子树的方案数。

\(dp\) 开两维,第一维是 \(i\) , 第二维是 $ 0 || 1 $ , \(0\) 代表此位置选 \(0\) , \(1\) 同上。

易得出:

\[dp[ \ i \ ][ \ 0 \ ] \times = ( dp[ \ j \ ][ \ 0 \ ] + dp[ \ j \ ][ \ 1 \ ] ) ; \]

\[dp[ \ i \ ][ \ 1 \ ] \times = dp[ \ j \ ][ \ 0 \ ] \]

然后我们在定义一个定义方式与 \(dp\) 相同的 \(f\) 数组 , 表示以 \(i\) 为根的整棵树的方案数。

定义前驱结点即后继节点的直系父亲 \(x\), 后继节点即儿子 \(y\).

我们假设(或可以看做已知)你已知 \(f_{x,0 \ and \ 1}\) ,因为你是要打 \(DFS\) 的, 那么我们可以在进这一遍递归之前求出之。

那么, $$ f_{ y , 1 } = \frac{ f_{x,0} }{ dp_{y,1}+dp_{y,0}} \times dp_{y,1}$$

\[f_{ y , 0 } = \left( \frac{ f_{x , 1}}{dp_{y,0}} + \frac{f_{ x , 0 } }{dp_{y,1} + dp_{y,0} } \right) \times dp_{y,0} \]

解释一下:

在你已知 \(x\) 是根的情况下,当 \(y\) 要取 \(1\) 时,由于一条边上至多选一个点,因此 \(x\)\(0\) ;

\(x\)\(0\) 时看上面 \(dp\) 数组的式子,你要求无 \(y\) 的方案数再乘以 \(y\) 取零的方案数;

\(y\) 取零同上。

然后推一下当边 \(i\) 是特殊边时的时候。

这时 \(f\)\(dp\) 均已知,考虑所有的情况:

\(x,y\) 分别是树根(令此特殊边为树根至其儿子的路径)

  1. \(x\) 取一, \(y\) 取一

则此时:

\[ans = \frac{f_{x,1}}{dp_{y,0}} \times dp_{y,1} \]

  1. \(x\) 取一, \(y\) 取零时

\[ans = \frac{f_{x,1}}{dp_{y,0}}\times dp_{y,0} \]

3.当 \(x\) 取零 , \(y\) 取一时

\[ans=\frac{f_{x,0}}{dp_{y,0}+dp_{y,1}} \times dp_{y,1} \]

\[\]


· \(Code\)

点击查看代码
#include <bits/stdc++.h>
#define qcin cin 
#define qcout cout  
#define int long long 
using namespace std ; 
const int N = 3e5 + 10 ; 
const int mod = 998244353 ; 
int dp[ N ][ 2 ] , f[ N ][ 2 ] ;  
int head[ N ] , cnt , n ; 
int father[ N ] ; 
class node
{
    public :
    int xe , ye ; 
}pe[ N ] ; 
class edge
{
    public:
        int next , to ; 
}e[ N ] ;
int Quick_Pow( int alpha , int beta )
{
    int ans = 1 ; 
    while ( beta > 0 )
    {
        if( beta & 1 ) ans = ( ans * alpha ) % mod ; 
        beta >>= 1 ; 
        alpha = ( alpha * alpha ) % mod ;  
    }
    return ans ; 
}
int inv( int alpha )
{
    return ( Quick_Pow( alpha , mod - 2 ) ) ; 
}
inline void add( int x , int y )
{
    cnt ++ ; 
    e[ cnt ].to = y ; 
    e[ cnt ].next = head[ x ] ; 
    head[ x ] = cnt ; 
}
void dfs1( int x , int fa )
{
    dp[ x ][ 0 ] = dp[ x ][ 1 ] = 1 ; 
    for ( int i = head[ x ] ; i ; i = e[ i ].next )
    {
        int y = e[ i ].to ; 
        if( y != fa )
        {
            father[ y ] = x ; 
            dfs1( y , x ) ; 
            dp[ x ][ 0 ] = ( dp[ x ][ 0 ] * ( dp[ y ][ 0 ] + dp[ y ][ 1 ] ) ) % mod ; 
			dp[ x ][ 1 ] = ( dp[ y ][ 0 ] * dp[ x ][ 1 ] ) % mod ; 
        }
    }
}
void dfs2( int x , int fa )
{
    for ( int i = head[ x ] ; i ; i = e[ i ].next ) 
    {
        int y = e[ i ].to ; 
        if( y != fa )
        {
            f[ y ][ 1 ] = f[ y ][ 0 ] = 1 ; 
            int inver0 = inv( dp[ y ][ 0 ] ) , inver1 = inv( dp[ y ][ 1 ] + dp[ y ][ 0 ] ) ; 
            int bemod1 = ( ( f[ x ][ 1 ] ) * ( inver0 ) ) % mod ;
            int bemod2 = ( f[ x ][ 0 ] * ( inver1 ) ) % mod ; 
            f[ y ][ 1 ] = ( dp[ y ][ 1 ] * bemod2 ) % mod ; 
            f[ y ][ 0 ] = ( dp[ y ][ 0 ] * ( bemod1 + bemod2 ) ) % mod ;  
            dfs2( y , x ) ; 
        }
    }
}
inline void Check_Shadow( int x , int y )
{
    int inv_y_0 , inv_y_1 , inv_y_01 ; 
    inv_y_0 = inv( dp[ y ][ 0 ] ) ; 
    inv_y_1 = inv( dp[ y ][ 1 ] ) ; 
    inv_y_01 = inv( dp[ y ][ 1 ] + dp[ y ][ 0 ] ) ; 
    int ans = 0 ; 
    ans = ( ans + ( ( ( f[ x ][ 1 ] * inv_y_0 ) % mod ) * dp[ y ][ 1 ] ) % mod ) ; 
    ans = ( ans + ( ( ( f[ x ][ 0 ] * inv_y_01 ) % mod ) * dp[ y ][ 1 ] ) % mod ) ; 
    ans = ( ans + f[ x ][ 1 ] ) % mod ; 
    cout << ans << '\n' ; 
}
signed main( )
{
    #ifndef ONLINE_JUDGE
        freopen( "1.in" , "r" , stdin ) ; 
        freopen( "1.out" , "w" , stdout ) ; 
    #endif
    cin >> n ; 
    int x , y ; 
    for ( int i = 1 ; i < n ; ++ i )
    {
        cin >> x >> y ; 
        pe[ i ].xe = x ; 
        pe[ i ].ye = y ; 
        add( x , y ) ; add( y , x ) ; 
    }
    int roo = 1 ; 
    dfs1( roo , 0 ) ; 
    f[ 1 ][ 1 ] = dp[ 1 ][ 1 ] ; 
    f[ 1 ][ 0 ] = dp[ 1 ][ 0 ] ; 
    dfs2( 1 , 0 ) ; 
    for ( int i = 1 ; i < n ; ++ i )
    {
        if( father[ pe[ i ].ye ] != pe[ i ].xe )
        {
            swap( pe[ i ].xe , pe[ i ].ye ) ; 
        }
        Check_Shadow( pe[ i ].xe , pe[ i ].ye ) ; 
    }
}

· 结尾撒花 \(\color{pink}✿✿ヽ(°▽°)ノ✿\)

posted @ 2024-02-22 07:05  HANGRY_Sol&Cekas  阅读(16)  评论(0编辑  收藏  举报