Solution -「多校聯訓」行列式
阿新 • • 發佈:2021-07-06
\(\mathcal{Description}\)
Link.
給定\(x,\{d_i\}_{i=1}^n,\{p_i\}_{i=2}^n,\{b_i\}_{i=2}^n,\{c_i\}_{i=2}^n\),構造矩陣 \(A=(a_{ij})_{n\times n}\):
\[a_{ij}=\begin{cases} b_j,&i=p_j\\ c_i,&j=p_i\\ d_i,&i=j\\ x,&\text{otherwise} \end{cases}. \]求 \(\det A\)。
\(n\le10^6\),\(p_i\in[1,i)\)。
\(\mathcal{Solution}\)
\(p_i\in[1,i),~i=2,3,\dots,n\)。
這是什麼?這是樹!
在這樣意識流的思考下,我們選擇將 \(A\) 看做鄰接矩陣,那麼自然地區分出了三類邊:
- 樹邊,以 \(1\) 位根,父親到兒子的邊權是 \(b\),兒子到父親的邊權是 \(c\);
- 自環,邊權為 \(d\);
- 非樹邊,邊權為 \(x\)。
矩陣中有大面積的常數 \(x\),利用 trick,從行列式的定義式的角度抵消大量貢獻。具體地,令 \(X=(x)_{n\times n},Y=A-X\),則
\[\begin{aligned} \det A&=\det (X+Y)\\ &=\sum_{\sigma}(-1)^{\tau(\sigma)}\prod_{i=1}^n(x+y_{i\sigma_i}). \end{aligned} \]此番強行構造出二項式相乘的形式,若累乘式中至少 \(2\)
回到圖上,考慮 \(\prod_{i=1}^n a_{i\sigma_i}\)
進一步,嘗試樹上 DP 求解這一形象問題。在計算逆序對 \(\tau(\sigma)\) 時,利用結論
設 \(\sigma\) 輪換分解後得到偶排列的的個數為 \(c\),則 \((-1)^c=(-1)^{\tau(\sigma)}\)。(易證。)
可以將 \((-1)\) 的貢獻直接乘到狀態的值中。具體來說,對於 \(u\) 點,狀態應有:
- 子樹內無 \(x\) 環;\(u\) 在子樹內的環中;
- 子樹內有 \(x\) 環;\(u\) 在子樹內的環中;
- 子樹內無 \(x\) 環;\(u\) 不在子樹內的環中且不在 \(x\) 環中;
- 子樹內有 \(x\) 環;\(u\) 不在子樹內的環中且不在 \(x\) 環中;
- 子樹內無 \(x\) 環;\(u\) 在頂點高於 \(u\) 的 \(x\) 環中,且選擇向上的樹邊;
- 子樹內無 \(x\) 環;\(u\) 在頂點高於 \(u\) 的 \(x\) 環中,且選擇向下的樹邊。
六種情況,分別寫出轉移式子即可。(當然也可以用多維狀態來讓思路更清晰。)
最終,複雜度 \(\mathcal O(n)\)。
\(\mathcal{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;
}