LOJ3626 「2021 集训队互测」愚蠢的在线法官

我们不妨设 \(A\)\([1,n]\) ,且为 \(\text{dfs}\) 序。那么显然该矩阵的第一列的列向量是完全相同的,我们再找到根节点的儿子,那么对于这些儿子的子树,子树内是该儿子的值,子树外是根节点的值。如此递归,我们发现每一次递归的时候,子树外的节点是不变的,子树内的节点是变成子树的根节点的权值。

我们发现行向量也是类似的,考虑进行一个类似于高斯消元的过程,每一个节点减去其父亲,我们就可以将每一个节点不属于该子树的部分消去。那么此时这个矩阵上的点就只有 \(O(n^2)\) 个了,但是你经过一定的位置交换可以发现,你只有右上方的位置有值,所以直接乘一波对角线就行了。

此时我们再来考虑一波如果 \(A\) 并不是 \(n\) 的排列,首先如果存在重复的部分,那么这一行一列直接可以被我们消成 \(0\) ,所以行列式就是 \(0\) ,这样只需要考虑如果有些位置不存在怎么办。我们考虑不存在的位置对于权值的影响,其实我们可以直接利用给一行或者一列加来使得位置消去,只不过需要注意考虑对于行列式的影响。

给一行或一列加好像不太好做?考虑对于一个节点 \(u\) ,如果这个节点不存在子树节点在 \(A\) 中或者只有一个存在,那么不会有位置的值与 \(u\) 相关;考虑如果存在两个,但是这两个互相包含,也不会存在值与 \(u\) 相关;考虑有多个互不包含的点,他会形成一个类似于如图的结构。

\[\begin{bmatrix} a_1 & x & x & z & \cdots & z\\ x & a_2 & x & z & \cdots & z\\ x & x & a_3 & z & \cdots & z\\ z & z & z & a_4 & \cdots & y\\ \cdots & \cdots & \cdots & \cdots & \cdots & \cdots\\ z & z & z & y & \cdots & a_n \end{bmatrix} \]

我们考虑利用递归处理这个结构(这个结构实际上就是一个树形结构),考虑在当前节点,我们先将最外层的点权(在上面的例子中就是 \(z\) )给减掉,这样我们选择递归求解给当前矩阵加上一个数 \(x\) 之后的行列式,可以证明是一个一次函数 \(ax+b\) (证明过程就是求解过程),我们此时考虑我们已经得到了两个子矩阵的一次函数,其中一个是上述的,另一个是 \(cx+d\) ,合并之后的结果可以证明是 \((ad+bc)x+bd\)

我们不妨先将这个函数的贡献拆开,不带 \(x\) 的部分便是本身的行列式,带 \(x\) 的部分便是加上的值的贡献,\(x\) 的几次项我们边可以看成是选择多少个位置加 \(x\) 。首先考虑如果是直接相乘这两个一次函数的话,便是不考虑 \(z\) 部分可以加上一个 \(x\) 的矩阵的行列式。考虑二次项相当于是选择两个位置加上 \(x\) ,且是一个在上方一个在下方,我们考虑必然存在唯一对应的一个排列,使得选择两个位置在 \(z\) 的与之一一对应且贡献相反,也就是说二次项的贡献是不存在的。同时需要注意 \(z\) 位置贡献的 \(x\) 的高次项,考虑如果次数大于等于 \(3\) ,根据鸽巢原理,其一定会有大于等于两个的位于同一块内,交换两个即可消去贡献。可以证明 \(z\) 部分不能贡献给一次项。

于是我们直接树上递归即可,甚至前面的消元都不需要了,膜拜李神。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
const int MOD=998244353;
int ADD(int x,int y){return x+y>=MOD?x+y-MOD:x+y;}
int SUB(int x,int y){return x-y<0?x-y+MOD:x-y;}
int TIME(int x,int y){return (int)(1ll*x*y%MOD);}
int n,m,v[N],cnt[N];
struct Edge{int nxt,to;}e[N<<1];int fir[N];
void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
pair<int,int> dp(int u,int fa,int tmp){
	tmp=SUB(v[u],v[fa]);pair<int,int> f;
	if(cnt[u]) f=make_pair(1,0);else f=make_pair(0,1);
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa) continue;
		pair<int,int> g=dp(v,u,tmp);
		f.first=ADD(TIME(f.first,g.second),TIME(f.second,g.first));
		f.second=TIME(f.second,g.second);
	}
	return f.second=ADD(f.second,TIME(f.first,tmp)),f;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i) scanf("%d",&v[i]);
	for(int i=1,x;i<=m;++i) scanf("%d",&x),cnt[x]++;
	for(int i=1,u,v;i<n;++i) scanf("%d%d",&u,&v),add(u,v,i<<1),add(v,u,i<<1|1);
	for(int i=1;i<=n;++i) if(cnt[i]>1) return printf("0\n"),0;
	return printf("%d\n",dp(1,0,0).second),0;
}
posted @ 2022-02-11 19:06  Point_King  阅读(219)  评论(0编辑  收藏  举报