• 【XSY3948】行列式(行列式,树形dp)


    题面

    行列式

    题解

    马神说:这可能是本场比赛最简单的一道题。

    黑人问号脸.jpg

    (这篇题解我很多地方写得很简略或很不严谨,所以如果有些地方看不懂请自己推一推)

    考虑构造矩阵 (B=(x)_{n imes n}),然后设矩阵 (C=A-B)

    那么矩阵 (C) 满足 (C_{p_i,i}=b_i-x)(C_{i,p_i}=c_i-x)(C_{i,i}=d_i-x),且其他地方的值都是 (0)

    然后我们要求的是 (det(A)=det(B+C)),考虑将其线性展开

    不会线性展开的可以利用下面的引理自己展开:

    引理:

    [egin{vmatrix} a_{1,1}&a_{1,2}&cdots&a_{1,n}\ vdots&vdots&&vdots\ a_{i-1,1}&a_{i-1,2}&cdots&a_{i-1,n}\ b_1+c_1&b_2+c_2&cdots&b_n+c_n\ a_{i+1,1}&a_{i+1,2}&cdots&a_{i+1,n}\ vdots&vdots&&vdots\ a_{n,1}&a_{n,2}&cdots&a_{n,n}\ end{vmatrix} =egin{vmatrix} a_{1,1}&a_{1,2}&cdots&a_{1,n}\ vdots&vdots&&vdots\ a_{i-1,1}&a_{i-1,2}&cdots&a_{i-1,n}\ b_1&b_2&cdots&b_n\ a_{i+1,1}&a_{i+1,2}&cdots&a_{i+1,n}\ vdots&vdots&&vdots\ a_{n,1}&a_{n,2}&cdots&a_{n,n}\ end{vmatrix} +egin{vmatrix} a_{1,1}&a_{1,2}&cdots&a_{1,n}\ vdots&vdots&&vdots\ a_{i-1,1}&a_{i-1,2}&cdots&a_{i-1,n}\ c_1&c_2&cdots&c_n\ a_{i+1,1}&a_{i+1,2}&cdots&a_{i+1,n}\ vdots&vdots&&vdots\ a_{n,1}&a_{n,2}&cdots&a_{n,n}\ end{vmatrix} ]

    证明:根据行列式的定义用乘法分配律即可简单证明。

    展开后,就能写成每行是 (B)(C) 对应行之一的 (2^n) 个矩阵的 (det) 的和。发现这 (2^n) 个矩阵中,那些选了大于一行 (B) 的矩阵肯定是没有贡献的,因为 (B) 是个全 (x) 矩阵,而我们又知道 “如果矩阵中有两行成比例,那么其行列式为 (0)”。

    所以要计算的矩阵中要么选了恰好一行 (B),要么没有选 (B)

    先考虑没有选 (B) 的情况,此时我们要计算的是 (det(C)=sumlimits_{pp}(-1)^{ au(pp_1,pp_2,cdots,pp_n)}C_{1,pp_1}C_{2,pp_2}cdots C_{n,pp_n})。(这里用 (pp) 只是为了和 (p) 区分开来)

    此时发现难以直接计算,就有一种比较神奇的做法:

    注意到条件 (1leq p_i<i),那么如果把 (p_i) 当做 (i) 的父亲的话,就会形成一棵树。

    由于行列式的定义中,我们每一行都要选一个位置乘起来,不妨设第 (i) 行选了 (C_{i,j})(只考虑 (C_{i,j} eq 0) 的情况)。

    那么我们不妨把选了 (C_{i,j}) 看做选了边 ((i,j)),发现选的边要么在树上,要么是树上某个点的自环。

    那么我们不妨给树上的边 ((i,p_i)) 加上边权 (c_i-x),边 ((p_i,i)) 加上边权 (b_i-x),同时再给树上的每一个点加上一条自环边,其边权为 (d_i-x)。(不妨仍称这个带有自环的图叫做 “树”)

    那这样选了 (C_{i,j}) 就相当于选了 ((i,j)) 的边权了。

    同时,行列式选完位置后,要保证每一行、每一列都恰好只选了一个位置。

    对应到树上,就代表树上选完边并对选的边统计每个点入度出度后(自环 ((u,u))(u) 的入度和出度各贡献 (1)),每个点的入度和出度恰好为 (1)

    这个时候会发现一个东西:如果我们选了一条边 ((i,j))(i eq j),即不考虑自环),那么我们一定也会选择边 ((j,i))。也就是说我们选边时一定是 ((i,p_i))((p_i,i)) 捆绑着同时选。

    证明比较简单,从树的叶子节点层一直往上推就行了。

    那这样就很好讨论了,直接树形dp即可。

    具体详见代码。

    有一个要注意的地方,就是如何确定每一种选择方案前面的系数((-1) 还是 (1))。

    假设我们选了 (k) 组捆绑的边,那么系数就是 ((-1)^k)

    原因(写得你可能看不懂):我们会把 ((i,p_i))((p_i,i)) 同时选,而这两个点在矩阵上是关于矩阵斜右向下的对角线对称的。不妨设我们选了一个点 (u=(x,y)),那么 (u) 的对称点 (u') 肯定也被选了。画画图可以得知,如果我们选的两个点 (u)(v)(v eq u'))构成逆序对,那么它们的对称点 (u')(v') 也会构成逆序对,此时 (-1) 就抵消了。所以只有 ((u,u')) 构成逆序对时才会有贡献,而 ((u,u')) 的数量就是我们选的捆绑的边的组数。

    那么既然系数和选的边的组数有关,那么系数就可以在树形dp时顺便处理了。

    而对于求选了恰好一行 (B) 的矩阵的行列式,假设选的是第 (i) 行,我们就把原来树上所有 (i) 的出边先去掉,再向所有点都连一条边权为 (x) 的边,然后也是要满足每个点的入度和出度恰好为 (1),要求不同选边方案的贡献的总和。

    这个我们同样也可以用树形dp来搞定。

    至于这时如何确定每一种选择方案前面的系数((-1) 还是 (1)),我们先称选的那条边权为 (x) 的边所在的环为 “(X) 环”。

    不妨设我们在 (X) 环上选了边 ((a_1,a_2),(a_2,a_3),cdots,(a_{k-1},a_k),(a_k,a_1)),在 (X) 环外选了边 ((b_1,c_1),(c_1,b_1),(b_2,c_2),(c_2,b_2),cdots,(b_l,c_l),(c_l,b_l)),那么我们要求的系数即为:

    [(-1)^{ au(b_1,c_1,b_2,c_2,cdots,b_l,c_l,a_1,a_2,cdots,a_{k-1},a_k)+ au(c_1,b_1,c_2,b_2,cdots,c_l,b_l,a_2,a_3,cdots,a_k,a_1)} ]

    我们只需讨论 ( au(b_1,c_1,b_2,c_2,cdots,b_l,c_l,a_1,a_2,cdots,a_{k-1},a_k)+ au(c_1,b_1,c_2,b_2,cdots,c_l,b_l,a_2,a_3,cdots,a_k,a_1)) 的奇偶性即可。

    记序列 (p_1=(b_1,c_1,b_2,c_2,cdots,b_l,c_l,a_1,a_2,cdots,a_{k-1},a_k)),将其分为左半部分 (pl_1=(b_1,c_1,b_2,c_2,cdots,b_l,c_l)) 和右半部分 (pr_1=(a_1,a_2,cdots,a_{k-1},a_k))

    同理得到 (p_2=(c_1,b_1,c_2,b_2,cdots,c_l,b_l,a_2,a_3,cdots,a_k,a_1))(pl_2=(c_1,b_1,c_2,b_2,cdots,c_l,b_l))(pr_2=(a_2,a_3,cdots,a_k,a_1))

    考虑 (pl_1) 中的数 (p_{1,i})(pr_1) 中的数 (p_{1,j}) 产生的逆序对集合 (P_1={(p_{1,i},p_{1,j})}),以及 (pl_2) 中的数 (p_{2,i})(pr_2) 中的数 (p_{2,j}) 产生的逆序对集合 (P_2={(p_{2,i},p_{2,j})}),不难发现这两个集合是完全相同的。因为 (pl_1) 中的数的集合和 (pl_2) 中的数的集合是完全相同的,而 (pr_1) 中的数的集合和 (pr_2) 中的数的集合也是完全相同的。

    所以 (P_1)(P_2) 对逆序对个数的贡献会因为只考虑奇偶性而被抵消掉。

    所以我们只需要考虑 (pl_1,pr_1,pl_2,pr_2) 它们各自的逆序对个数之和的奇偶性即可。

    (pl_1)(pl_2) 的贡献直接按我们第一种情况(即求 (det(C)) 的情况)处理即可,而对于求 (ig( au(pr_1)+ au(pr_2)ig) mod 2)

    [egin{aligned} au(pr_1)+ au(pr_2) =& au(a_1,a_2,cdots,a_{k-1},a_k)+ au(a_2,a_3,cdots,a_k,a_1)\ =& ext{$a_1\,$和$\,(a_2,cdots,a_{k-1},a_k)\,$的逆序对个数}+ ext{$(a_2,cdots,a_{k-1},a_k)\,$和$\,a_1\,$的逆序对个数}+\ &2 au(a_2,cdots,a_{k-1},a_k)\ equiv& ext{$a_1\,$和$\,(a_2,cdots,a_{k-1},a_k)\,$的逆序对个数}+ ext{$(a_2,cdots,a_{k-1},a_k)\,$和$\,a_1\,$的逆序对个数}\ equiv&k-1pmod 2 end{aligned} ]

    那么也可以在树形dp时顺便处理了。

    代码如下:

    #include<bits/stdc++.h>
    
    #define N 1000010
    
    using namespace std;
    
    namespace modular
    {
    	const int mod=1000000007;
    	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    	inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
    	inline int mul(int x,int y){return 1ll*x*y%mod;}
    }using namespace modular;
    
    inline int read()
    {
    	int x=0,f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		x=(x<<1)+(x<<3)+(ch^'0');
    		ch=getchar();
    	}
    	return x*f;
    }
    
    int n,x,d[N];
    int cnt,head[N],nxt[N<<1],to[N<<1],bb[N<<1],cc[N<<1];
    int f[N][6],g[6];
    //不妨设我们选的边权长度为x的边为(u,v),那么下面的“X路径”是指树上的路径v->u(即X环去掉(u,v)的那部分)
    //f[u][0]:已处理完u子树内的边,u子树内无X路径,u需要选与父亲相连的那条双向边(即两条单向边都选上)
    //f[u][1]:已处理完u子树内的边,u子树内无X路径,u不能选与父亲相连的那条双向边
    //f[u][2]:已处理完u子树内的边,u子树包含完整的X路径,u需要选与父亲相连的那条双向边
    //f[u][3]:已处理完u子树内的边,u子树包含完整的X路径,u不能选与父亲相连的那条双向边
    //f[u][4]:已处理完u子树内的边,X路径的终点在u子树内,起点在u子树外
    //f[u][5]:已处理完u子树内的边,X路径的起点在u子树内,终点在u子树外
    
    void adde(int u,int v,int b,int c)
    {
    	to[++cnt]=v;
    	bb[cnt]=b,cc[cnt]=c;
    	nxt[cnt]=head[u];
    	head[u]=cnt;
    }
    
    void dfs(int u)
    {
    	f[u][0]=f[u][4]=f[u][5]=1;
    	f[u][1]=d[u];
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i],b=bb[i],c=cc[i];
    		dfs(v);
    		memset(g,0,sizeof(g));
    		g[0]=mul(f[u][0],f[v][1]);
    		g[1]=mul(f[u][1],f[v][1]);
    		g[1]=add(g[1],mul(f[u][0],dec(0,mul(mul(b,c),f[v][0]))));
    		g[2]=mul(f[u][2],f[v][1]);
    		g[2]=add(g[2],mul(f[u][0],f[v][3]));
    		g[3]=mul(f[u][3],f[v][1]);
    		g[3]=add(g[3],mul(f[u][1],f[v][3]));
    		g[3]=add(g[3],mul(f[u][0],dec(0,mul(mul(b,c),f[v][2]))));
    		g[3]=add(g[3],mul(f[u][2],dec(0,mul(mul(b,c),f[v][0]))));
    		g[3]=add(g[3],mul(f[u][5],dec(0,mul(mul(x,b),f[v][4]))));
    		g[3]=add(g[3],mul(f[u][4],dec(0,mul(mul(x,c),f[v][5]))));
    		g[4]=mul(f[u][4],f[v][1]);
    		g[4]=add(g[4],mul(f[u][0],dec(0,mul(b,f[v][4]))));
    		g[5]=mul(f[u][5],f[v][1]);
    		g[5]=add(g[5],mul(f[u][0],dec(0,mul(c,f[v][5]))));
    		memcpy(f[u],g,sizeof(f[u]));
    	}
    	f[u][3]=add(f[u][3],mul(x,f[u][0]));//X路径是自环
    }
    
    int main()
    {
    	n=read(),x=read();
    	for(int i=1;i<=n;i++) d[i]=dec(read(),x);
    	for(int i=2;i<=n;i++)
    	{
    		int p=read(),b=dec(read(),x),c=dec(read(),x);
    		adde(p,i,b,c);
    	}
    	dfs(1);
    	printf("%d
    ",add(f[1][1],f[1][3]));
    	return 0;
    }
    /*
    3 1 
    1 1 1
    1 2 3
    1 4 5
    */
    
  • 相关阅读:
    区间DP中的环形DP
    hdu 5251 包围点集最小矩形 ***
    hdu 4858 水题
    hdu 3530 单调队列 **
    hdu 3338 最大流 ****
    hdu 2732 最大流 **
    hdu 5233 离散化 **
    hdu 3555 数位dp *
    zoj 3469 区间dp **
    2015 安徽程序设计省赛总结
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14508522.html
Copyright © 2020-2023  润新知