题解:P11567 建造军营 II
联考迫真 NOIP 模拟赛 T3(
学完了 cxy 的 APIO2025 课件来做的,复杂度比原 std 优一点。
题意
给定一个 \(n\) 个点 \(m\) 条边的无向连通图 \(G=(V,E)\),再给定 \(k\) 个点对 \((a_i,b_i)\)。对于一个连通边子图 \(G'=(V,E')\),可以选择其中的一个边子集,一条边可选当且仅当不存在 \(1\le i\le k\) 使得存在从 \(a_i\) 到 \(b_i\) 且经过该边的边简单路径,定义 \(G'\) 的权值为选择边子集的方案数。
求所有连通边子图的权值和,对 \(10^9+7\) 取模。
\(n\le 16\)。
题解
容易发现权值计算与边双有关,具体地,将边双缩点后,对于每一对点对 \((a_i,b_i)\),将 \(a_i,b_i\) 所在边双之间的所有边标黑,则可以进行选择的边为不被标黑的边。
边双连通-连通变换
首先必须要会求边双连通子图数。先考虑如下问题:定义 \(G=\text{Trans}(F,c)\),其中 \(F,G\) 分别为集合幂级数,\(c\) 为一常数。对于集合 \(s\) 定义其划分 \(s_{1\sim k}\) 的权值为 \(c^{k-1}\prod_{i=1}^k F_{s_i}\) 再乘上用 \(k-1\) 条边将它们连通的方案数,即每选择一条边合并两个连通块带来 \(c\) 的贡献。需要返回的集合幂级数 \(G\) 的 \(s\) 项系数即为 \(s\) 所有划分的权值和。
考虑按照两端点编号的最大值的顺序进行加边。设集合幂级数 \(p_i\) 表示允许加两端点编号均 \(\le i\) 的边时的答案。对于一个点集 \(S\),考虑删去新加边中一端点为 \(i\) 的边,形成若干个连通块 \(S=P\cup T_1\cup T_2\cup\cdots\cup T_k\),其中 \(P\) 包含点 \(i\),其余连通块均不包含。
设集合幂级数 \(q_{i-1,s}=[i\notin S]p_{i-1,s}\cdot cof(i,S)\cdot c\),其中 \(cof(i,S)\) 表示 \(i\) 与集合 \(S\) 内编号 \(\le i\) 的点的边数。容易发现有 \(p_{i,s}=(p_{i-1}\cdot \exp q_{i-1})_s\),注意该式只有在 \(i\in S\) 的项成立,对于其余项平凡地有 \(p_{i,s}=p_{i-1,s}\)。
令 \(F_s\) 表示 \(s\) 内连通子图数,则 \(\text{Trans}(F,-1)\) 即为边双连通子图数的集合幂级数。考虑 \(s\) 的一个子图,若其中存在割边,则这条割边必然分别在 \(F_s\) 处和合并时贡献 \(1\) 与 \(-1\),即该方案不会被计入。求解 \(F_s\) 是经典问题,直接求 $\ln $ 即可。
时间复杂度 \(O(2^nn^3)\)。
本题解法
我们希望将原图划分为若干黑边连通块与白边双连通分量,且这些连通块之间均用白边连接。
先求出每个集合 \(s\) 作为黑边连通块的方案数。令 \(F_s\) 表示 \(s\) 内连通子图数,注意不能存在 \(a_i,b_i\) 使得其中恰有一个点在 \(s\) 内。考虑 \(F'=\text{Trans}(F,-1)\),则若一种方案中存在白边,其必然会被抵消。
接下来需要求出集合 \(s\) 作为白边双连通分量时的权值之和。令 \(G_s\) 表示 \(s\) 内连通子图权值和,注意不能存在任意 \(a_i\) 或 \(b_i\) 在 \(s\) 内。\(G\) 的求解仍可用求 \(\ln\) 解决,而 \(G'=\text{Trans}(G,-2)\) 即为所求。
最后只需调用 \(\text{Trans}(F'+G',2)\) 合并连通块即可。
时间复杂度 \(O(2^nn^3)\),经卡常可获得最优解。
附一份未经卡常的代码:
const int S=1<<16,N=16+2,M=N*N,mod=1e9+7;
int n,m,k,u,pw2[M],pw3[M],a[N],ct[S],c1[S],c2[S],T[S],F[S],G[S],A[S],B[S];
inline void Trans(int *F,int c){
static int A[S],G[S];
for(int i=0;i<n;i++){
for(int s=0;s<u;s++)
A[s]=s>>i&1?0:1ll*c*F[s]%mod*__builtin_popcount(a[i]&s)%mod;
Poly::Exp(A,A);
Poly::Mul(A,F,G);
for(int s=0;s<u;s++)
F[s]=s>>i&1?G[s]:F[s];
}
}
int main(){
// freopen("ex_war4.in","r",stdin);
n=read(),k=read(),u=1<<n;
Prefix(u),Poly::init(u);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
int c=getc()-'0';
if(i<=j||!c) continue;
a[i]|=1<<j,ct[(1<<i)|(1<<j)]++;
}
while(k--){
int u=read()-1,v=read()-1;
c1[1<<u]++,c1[1<<v]++,c1[(1<<u)|(1<<v)]-=2;
c2[1<<u]++,c2[1<<v]++;
}
pw2[0]=pw3[0]=1;
for(int i=1;i<=n*n;i++)
pw2[i]=add(pw2[i-1],pw2[i-1]),
pw3[i]=3ll*pw3[i-1]%mod;
for(int i=0;i<n;i++)
for(int s=0;s<u;s++)
if(s>>i&1)
ct[s]+=ct[s^(1<<i)],
c1[s]+=c1[s^(1<<i)],
c2[s]+=c2[s^(1<<i)];
for(int s=0;s<u;s++)
T[s]=pw2[ct[s]];
Poly::Ln(T,F);
for(int s=1;s<u;s++)
if(!c1[s])
A[s]=F[s];
Trans(A,mod-1);
for(int s=0;s<u;s++)
T[s]=pw3[ct[s]];
Poly::Ln(T,F);
Trans(F,mod-2);
for(int s=1;s<u;s++)
B[s]=c2[s]?A[s]:F[s];
Trans(B,2);
write(B[u-1]);
flush();
}

浙公网安备 33010602011771号