题目链接
题目解法
第一眼可以发现这道题要按照边双缩点
在一个边双联通分量内的点即使去掉一条边也可以互相到达
于是我们可以把这道题变成一个树上的问题,最后答案只要乘上
2
边双内的边数
2^{边双内的边数}
2边双内的边数 的系数就可以了
下文把双联通过后的图中的边双成为点,把看守的边称为连接的边
我们知道每个作为军营的点之间必须有边连接
考虑树形
D
P
DP
DP
首先一个显然的式子是
d
p
[
u
]
[
0
/
1
]
dp[u][0/1]
dp[u][0/1] 表示在
u
u
u 的子树中,是(1)否(0)选点的方案数,后文用
v
v
v 来表示
u
u
u 的一个儿子
后面一维
0
/
1
0/1
0/1 是为了转移时是否一定需要取
u
u
u 和
v
v
v 之间的边
考虑转移
d
p
[
u
]
[
0
]
=
2
u
的子树中包含的树边
dp[u][0]=2^{u的子树中包含的树边}
dp[u][0]=2u的子树中包含的树边
这个比较好理解
主要是
d
p
[
u
]
[
1
]
dp[u][1]
dp[u][1]
如果不考虑必须选一个点,方案数是
2
s
i
z
[
u
]
∗
(
d
p
[
v
]
[
0
]
∗
2
+
d
p
[
v
]
[
1
]
)
2^{siz[u]}*(dp[v][0]*2+dp[v][1])
2siz[u]∗(dp[v][0]∗2+dp[v][1])
其中
s
i
z
[
u
]
siz[u]
siz[u] 表示边双
u
u
u 的大小,这个式子也不难理解
于是我们考虑必须选点
可以从前往后考虑必须选
v
v
v,
v
v
v 之前的点需要被固定为不选,这样可以做到不重不漏
考虑最终的答案是什么
我们发现如果在
l
c
a
lca
lca 处统计会算重,为什么?
考虑下面的情况:
我们发现
u
u
u 与
l
c
a
lca
lca 处会多算
所以我们可以换一种统计的方法
即在第一条不与上面相连的边处统计
这样答案就是
∑
i
=
1
n
d
p
[
i
]
[
1
]
∗
2
不在
i
中的树边的数量
−
1
\sum_{i=1}^{n} dp[i][1]*2^{不在 i 中的树边的数量-1}
∑i=1ndp[i][1]∗2不在i中的树边的数量−1
即
i
i
i 与
f
a
t
h
e
r
[
i
]
father[i]
father[i] 的边必须不连,这里根要特判一下
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N(500100),M(2000100),P(1e9+7);
int n,m,cnt,bs[M],dp[N][2];
int treeE[N],totV[N];
int e[M],ne[M],h[N],idx;
int dfn[N],low[N],dfs_clock,scc_num[N],scc_cnt;
int stk[N],top;
vector<int> G[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
RR=-1;
for(;isdigit(ch);ch=getchar())
FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
void tarjan(int u,int from){
dfn[u]=low[u]=++dfs_clock,stk[++top]=u;
for(int i=h[u];~i;i=ne[i]){
if(i==(from^1)) continue;
int v=e[i];
if(!dfn[v]){
tarjan(v,i);
low[u]=min(low[u],low[v]);
}
else
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
scc_cnt++;
while(stk[top]!=u)
scc_num[stk[top--]]=scc_cnt,totV[scc_cnt]++;
scc_num[stk[top--]]=scc_cnt,totV[scc_cnt]++;
}
}
int qmi(int a,int b,int p){
int res=1;
for(;b;b>>=1){
if(b&1) res=(LL)res*a%p;
a=(LL)a*a%p;
}
return res;
}
void dfs(int u,int fa){
int tot=bs[totV[u]];
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v==fa) continue;
dfs(v,u);
treeE[u]+=treeE[v]+1;
tot=(LL)tot*(dp[v][0]*2%P+dp[v][1])%P;
}
tot=(LL)tot*qmi(bs[totV[u]],P-2,P)%P;
dp[u][1]=(LL)tot*(bs[totV[u]]-1+P)%P;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v==fa) continue;
tot=(LL)tot*qmi(dp[v][0]*2%P+dp[v][1],P-2,P)%P;
dp[u][1]=(dp[u][1]+(LL)tot*dp[v][1]%P)%P;
tot=(LL)tot*dp[v][0]*2%P;
}
dp[u][0]=bs[treeE[u]];
}
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int main(){
bs[0]=1;
for(int i=1;i<M;i++) bs[i]=bs[i-1]*2%P;
n=read(),m=read();
memset(h,-1,sizeof(h));
for(int i=1,a,b;i<=m;i++){
a=read(),b=read();
add(a,b),add(b,a);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i,-1);
int inE=0;
for(int i=1;i<=n;i++)
for(int j=h[i];~j;j=ne[j]){
int a=scc_num[i],b=scc_num[e[j]];
if(a!=b) G[a].push_back(b);
else inE++;
}
dfs(1,-1);
int ans=0;
for(int i=1;i<=scc_cnt;i++){
int left=max(0,scc_cnt-2-treeE[i]);
ans=(ans+(LL)bs[left]*dp[i][1]%P)%P;
}
printf("%d",(LL)ans*bs[inE>>1]%P);
return 0;
}