题目
给出一个仙人掌,问邻接矩阵的行列式……
n
≤
1
e
5
n\leq 1e5
n≤1e5
思考历程
?
不存在的
直接高斯消元拿30分跑路。
正解
先分析一波行列式的性质。
行列式的式子长这样:
∑
{
p
i
}
(
−
1
)
r
e
v
(
p
)
∏
i
A
i
,
p
i
\sum_{\{p_i\}}(-1)^{rev(p)}\prod_{i}A_{i,p_i}
{pi}∑(−1)rev(p)i∏Ai,pi
重要性质:对于一个排列
{
p
i
}
\{p_i\}
{pi},
i
i
i向
p
i
p_i
pi连边,
(
−
1
)
偶
环
个
数
=
(
−
1
)
r
e
v
(
p
)
(-1)^{偶环个数}=(-1)^{rev(p)}
(−1)偶环个数=(−1)rev(p)
证明:
众所周知,矩阵交换两行(或两列)之后,行列式取反。
假如将两个点的编号交换,那么相当于交换了两行和两列,行列式不变。
于是形成了若干个优美的环,每个的环的形式为 1 → 2 , 2 → 3 , . . . , L → 1 1\to 2,2\to 3,...,L\to 1 1→2,2→3,...,L→1。
只有最后一个有 ( − 1 ) L − 1 (-1)^{L-1} (−1)L−1的贡献,于是只有偶环有贡献。
偶环个数就是 − 1 -1 −1的指数。
将式子放到邻接矩阵中。
如果有贡献,那么
A
i
,
p
i
A_{i,p_i}
Ai,pi不为
0
0
0,即
(
i
,
p
i
)
∈
E
(i,p_i)\in E
(i,pi)∈E
转化一下模型,可以变成这样:在图中选出若干个点不相交的环和若干个一条边连接着的点对,奇环贡献为
2
2
2,偶环贡献为
−
2
-2
−2,点对的贡献为
−
1
-1
−1。求所有方案的贡献和。
建出圆方树之后随便DP。
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
#define M 200010
#define ll long long
#define mo 998244353
int n,m;
struct EDGE{
int to;
EDGE *las;
};
struct Graph{
EDGE *last[N*2];
void link(int u,int v){
EDGE *nw=new EDGE;
*nw={v,last[u]};
last[u]=nw;
}
} G,F;
int dfn[N],low[N],nowdfn;
int st[N],tp;
int num;
void tarjan(int x,int fa){
low[x]=dfn[x]=++nowdfn;
st[++tp]=x;
for (EDGE *ei=G.last[x];ei;ei=ei->las){
if (ei->to==fa)
continue;
if (dfn[ei->to])
low[x]=min(low[x],dfn[ei->to]);
else{
tarjan(ei->to,x);
low[x]=min(low[x],low[ei->to]);
if (low[ei->to]==dfn[x]){
++num;
for (;st[tp]!=ei->to;--tp)
F.link(num,st[tp]);
F.link(num,ei->to);
--tp;
F.link(x,num);
}
else if (low[ei->to]==dfn[ei->to]){
F.link(x,ei->to);
--tp;
}
}
}
}
int q[N];
ll f[N*2][2],h[N*2][2];
//f[x][0/1] : circle:whether x is chose square:whether fa is chose
void dfs(int x,int fa){
if (x<=n){
f[x][0]=1,f[x][1]=0;
for (EDGE *ei=F.last[x];ei;ei=ei->las){
dfs(ei->to,x);
int y=ei->to;
if (y<=n){
f[x][1]=((-f[x][0]*f[y][0]+f[x][1]*f[y][1])%mo+mo)%mo;
f[x][0]=f[x][0]*f[y][1]%mo;
}
else{
f[x][1]=(f[x][0]*f[y][1]+f[x][1]*f[y][0])%mo;
f[x][0]=f[x][0]*f[y][0]%mo;
}
}
}
else{
int k=0;
for (EDGE *ei=F.last[x];ei;ei=ei->las)
dfs(ei->to,x);
for (EDGE *ei=F.last[x];ei;ei=ei->las)
q[++k]=ei->to;
ll g=(k+1&1?2:mo-2);
for (int i=1;i<=k;++i)
g=g*f[q[i]][0]%mo;
f[x][1]+=g;
h[1][0]=f[q[1]][0],h[1][1]=f[q[1]][1];
for (int i=2;i<=k;++i){
int y=q[i];
h[i][0]=h[i-1][1]*f[y][0]%mo;
h[i][1]=((-h[i-1][0]*f[y][0]+h[i-1][1]*f[y][1])%mo+mo)%mo;
}
f[x][0]+=h[k][1];
f[x][1]+=-h[k][0]+mo;
h[1][0]=0,h[1][1]=-f[q[1]][0]+mo;
for (int i=2;i<=k;++i){
int y=q[i];
h[i][0]=h[i-1][1]*f[y][0]%mo;
h[i][1]=((-h[i-1][0]*f[y][0]+h[i-1][1]*f[y][1])%mo+mo)%mo;
}
(f[x][1]+=h[k][1])%=mo;
}
}
int main(){
freopen("cactus.in","r",stdin);
freopen("cactus.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=m;++i){
int u,v;
scanf("%d%d",&u,&v);
G.link(u,v),G.link(v,u);
}
num=n;
tarjan(1,0);
dfs(1,0);
printf("%lld\n",f[1][1]);
return 0;
}
总结
之前在想行列式相关的时候,基本上没有从定义式上去思考……
学到了……