Loading

愚蠢的在线法官

题目描述

给定一棵树,每个点有权值\(v_i\),以及一个大小为\(k\)的数列\(A\)
询问:

\[\begin{bmatrix} v_{LCA(A_1,A_1)} & v_{LCA(A_1,A_2)} & v_{LCA(A_1,A_3)} & ……\\ v_{LCA(A_2,A_1)} & v_{LCA(A_2,A_2)} & v_{LCA(A_2,A_3)} & ……\\ …… & …… & …… & ……\\ v_{LCA(A_k,A_1)} & v_{LCA(A_k,A_2)} & v_{LCA(A_k,A_3)} & ……\\ \end{bmatrix} \tag{2} \]

的行列式。

\(k,n\leq 5\times 10^{5}\)

解题思路

\(a\to b\)表示\(a\)\(b\)的祖先。

首先\(A\)中如果有两个数相同,那么矩阵中就有两行相同,那么行列式为0.

考虑一个特殊的情况,\(k=n\)

考虑对于每个点求一个数组\(f\),满足 $$\forall u,v\in n, v_{LCA(u,v)}=\sum_{i=1}^nf[i][i\to LCA(u,v)]$$。

易得\(f[i]=v[i]-v[p[i]]\),其中\(p[i]\)\(i\)的父亲。

我们发现\([i\to LCA(u,v)]\Leftrightarrow [i\to u][i\to v]\)

那么我们可以改写成 $$v_{LCA(u,v)}=\sum_{i=1}^nf[i][i\to u][i\to v]$$

容易发现这个式子可以写成矩阵乘法的形式,因此答案矩阵可以拆成两个矩阵:$$C_{i,j}=[j\to i]f[j],D_{i,j}=[i\to j] $$

由行列式的性质可得,方阵矩阵的行列式等于两个新方阵矩阵的行列式乘积。

如果我们事先把所有点按照深度从小到大排序,那么对于一个\(i\),所有\([j\to i]\)\(j\)都在\(i\)的左边,因此 \(C,D\) 都是三角矩阵,所以行列式就是对角线的乘积.

如果\(k<n\),那么就不是方阵了,不能用上述的公式。

但是依然可以拆成一个\(k\times n\)的矩阵和一个\(n\times k\)的矩阵。

对于一个\(k\times n\)的矩阵\(A\),和\(n\times k\)的矩阵\(B\),由柯西-比内公式:

\[det(AB)=\sum_{S\subset \lbrace 1,2,……n,\rbrace ,|S|=k}det(A_S)det(B_S) \]

其中 \(A_S\)为取出\(A\)中位于\(S\)中的列形成的方阵,\(B_S\)同理。

考虑行列式的定义式:\(det(A)=\sum_{p}(-1)^{sign(p)}\prod_{i=1}^n A_{i,p_i}\)

那么上式的组合意义就是,选出一个点集\(S\),称其为未匹配点,让\(A\)中的点和这些点匹配,满足\(A\)中点匹配的点必须是其祖先,求所有方案的\(S\)中的点的\(f\)的乘积和乘上方案数,再乘上各自排列的逆序对数的和。

对于一个合法匹配\(P\),若存在\(i,j\)使得交换\(P_i,P_j\)之后,\(P\)依然合法则称\(P\)为可交换的匹配。

引理1:所有可交换的匹配对答案的贡献和为0.

证明大概是找到一个双射,然后因为交换之后逆序对数奇偶性该变,但是答案不变,所以正好抵消。

因此我们只需要探讨所有不可交换的匹配。

引理2:对于一个选出的\(S\),若其存在某个\(A\)中的点,满足其祖先内至少有两个点属于\(S\),那么该\(S\)的所有匹配均可交换。

显然。

推论:若不满足上述条件,那么\(S\)的所有匹配均不可交换,我们称\(S\)为不可交换的。

引理3:对于一个存在匹配且不可交换的\(S\),其匹配唯一。

考虑对于每个\(A\)中的点,匹配其被选出的离他最近的祖先,若某两个点匹配的一样,则该点集是可交换的,矛盾。因此这是一个合法的匹配。

同时该匹配唯一,因为对于一个点,若其不匹配离他最近的祖先,那么必须要匹配一个深度更小的点,那么他原本的祖先的子树内必定有至少一个点无法被匹配,因此一定不合法。

因此对于选出的一个不可交换的\(S\)\(C\)矩阵和\(D\)矩阵都只能选出唯一一个匹配,因此逆序对数相同,就不需要考虑正负了。

那么现在就变成选出一个点集\(S\),满足\(S\)不可交换且存在匹配,求所有点的\(f\)乘积之和。

树形DP一下就好了。

时间复杂度\(O(n)\)


#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+7;
const int mod = 998244353;
struct edge
{
	int y,next;
}e[2*N];
int Link[N],t=0;
void add(int x,int y)
{
	e[++t].next=Link[x];
	e[t].y=y;
	Link[x]=t;
}
int f[N],g[N];
bool ins[N];
int n,k;
int v[N];
inline int plu(int a,int b){return (a+b>=mod?a+b-mod:a+b);}
void dfs(int x,int pre)
{
	f[x]=1;
	for(int i=Link[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==pre)continue;
		dfs(y,x);
		g[x]=plu(1ll*f[x]*g[y]%mod,1ll*g[x]*f[y]%mod);
		f[x]=1ll*f[x]*f[y]%mod;
	}
	if(ins[x])
	{
		g[x]=f[x];
		f[x]=1ll*f[x]*(v[x]-v[pre]+mod)%mod;
	}
	else f[x]=plu(f[x],1ll*g[x]*(v[x]-v[pre]+mod)%mod);
	
}
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)scanf("%d",&v[i]);
	bool flag=0;
	while(k--)
	{
		int x;
		scanf("%d",&x);
		if(ins[x]) flag=1;
		ins[x]=1;
	}
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d %d",&x,&y);
		add(x,y);
		add(y,x);
	}
	if(flag)
	{
		cout<<0;
		return 0;
	}
	dfs(1,0);
	cout<<f[1];
	return 0;
}

posted @ 2023-01-13 14:00  Larunatrecy  阅读(51)  评论(0)    收藏  举报