Live2D

Solution -「多校联训」行列式

Description

  Link.

  给定 x,{di}i=1n,{pi}i=2n,{bi}i=2n,{ci}i=2n,构造矩阵 A=(aij)n×n

aij={bj,i=pjci,j=pidi,i=jx,otherwise.

detA

  n106pi[1,i)

Solution

pi[1,i), i=2,3,,n

  这是什么?这是树!

  在这样意识流的思考下,我们选择将 A 看做邻接矩阵,那么自然地区分出了三类边:

  • 树边,以 1 位根,父亲到儿子的边权是 b,儿子到父亲的边权是 c
  • 自环,边权为 d
  • 非树边,边权为 x

  矩阵中有大面积的常数 x,利用 trick,从行列式的定义式的角度抵消大量贡献。具体地,令 X=(x)n×n,Y=AX,则

detA=det(X+Y)=σ(1)τ(σ)i=1n(x+yiσi).

此番强行构造出二项式相乘的形式,若累乘式中至少 2 项选择了 x,不妨设选择 x 的位置为 {q1,q2,,qk} (k>1),那么于 σ 中交换 σq1σq2 的位置得到 σ,在新的累乘式中同样选择 {q1,q2,,qk}x,而 (1)τ(σ)+τ(σ)=0,并且 σσ 两两对应抵消,所以这样的方案是没有贡献的!

  回到图上,考虑 i=1naiσi 的组合意义:每个结点 i 选择邻接边 i,σi,得到边权的乘积。所选的边显然构成若干简单环,而在排除至多出现一次的含有非树边 x 的环,其余环必然是自环或树边二元环!(注意含非树边的环亦可为这两种情况。)

  进一步,尝试树上 DP 求解这一形象问题。在计算逆序对 τ(σ) 时,利用结论

σ 轮换分解后得到偶排列的的个数为 c,则 (1)c=(1)τ(σ)。(易证。)

可以将 (1) 的贡献直接乘到状态的值中。具体来说,对于 u 点,状态应有:

  • 子树内无 x 环;u 在子树内的环中;
  • 子树内有 x 环;u 在子树内的环中;
  • 子树内无 x 环;u 不在子树内的环中且不在 x 环中;
  • 子树内有 x 环;u 不在子树内的环中且不在 x 环中;
  • 子树内无 x 环;u 在顶点高于 ux 环中,且选择向上的树边;
  • 子树内无 x 环;u 在顶点高于 ux 环中,且选择向下的树边。

六种情况,分别写出转移式子即可。(当然也可以用多维状态来让思路更清晰。)

  最终,复杂度 O(n)

Code

/*~Rainybunny~*/

#include <cstdio>

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

inline int rint() {
    int x = 0, f = 1, s = getchar();
    for ( ; s < '0' || '9' < s; s = getchar() ) f = s == '-' ? -f : f;
    for ( ; '0' <= s && s <= '9'; s = getchar() ) x = x * 10 + ( s ^ '0' );
    return x * f;
}

template<typename Tp>
inline void wint( Tp x ) {
    if ( x < 0 ) putchar( '-' ), x = -x;
    if ( 9 < x ) wint( x / 10 );
    putchar( x % 10 ^ '0' );
}

const int MAXN = 1e6, MOD = 1e9 + 7;
int n, ecnt, head[MAXN + 5], x, b[MAXN + 5], c[MAXN + 5], d[MAXN + 5];
int f[MAXN + 5][6];
struct Edge { int to, nxt; } graph[MAXN * 2 + 5];

inline void subeq( int& a, const int b ) { ( a -= b ) < 0 && ( a += MOD ); }
inline int sub( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
inline int mul( const long long a, const int b ) { return int( a * b % MOD ); }
inline int add( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
inline void addeq( int& a, const int b ) { ( a += b ) >= MOD && ( a -= MOD ); }

inline void link( const int s, const int t ) {
    graph[++ecnt] = { t, head[s] }, head[s] = ecnt;
}

inline void solve( const int u ) {
    f[u][0] = f[u][4] = f[u][5] = 1, f[u][1] = d[u];
    for ( int i = head[u], v; i; i = graph[i].nxt ) {
        solve( v = graph[i].to );
        static int tmp[6];
        tmp[0] = mul( f[u][0], f[v][1] );

        tmp[1] = mul( f[u][1], f[v][1] );
        subeq( tmp[1], mul( mul( f[u][0], f[v][0] ), mul( b[v], c[v] ) ) );
        
        tmp[2] = mul( f[u][2], f[v][1] );
        addeq( tmp[2], mul( f[u][0], f[v][3] ) );
        
        tmp[3] = mul( f[u][3], f[v][1] );
        addeq( tmp[3], mul( f[u][1], f[v][3] ) );
        subeq( tmp[3], mul( mul( f[u][0], f[v][2] ), mul( b[v], c[v] ) ) );
        subeq( tmp[3], mul( mul( f[u][2], f[v][0] ), mul( b[v], c[v] ) ) );
        subeq( tmp[3], mul( mul( f[u][4], f[v][5] ), mul( x, c[v] ) ) );
        subeq( tmp[3], mul( mul( f[u][5], f[v][4] ), mul( x, b[v] ) ) );
    
        tmp[4] = mul( f[u][4], f[v][1] );
        subeq( tmp[4], mul( mul( f[u][0], f[v][4] ), b[v] ) );

        tmp[5] = mul( f[u][5], f[v][1] );
        subeq( tmp[5], mul( mul( f[u][0], f[v][5] ), c[v] ) );

        rep ( j, 0, 5 ) f[u][j] = tmp[j];
    }
    addeq( f[u][3], mul( x, f[u][0] ) );
}

int main() {
    freopen( "B.in", "r", stdin );
    freopen( "B.out", "w", stdout );

    n = rint(), x = rint();
    rep ( i, 1, n ) d[i] = sub( rint(), x );
    rep ( i, 2, n ) {
        link( rint(), i ), b[i] = sub( rint(), x ), c[i] = sub( rint(), x );
    }

    solve( 1 );

    wint( add( f[1][1], f[1][3] ) ), putchar( '\n' );
    return 0;
}

posted @   Rainybunny  阅读(104)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示