NFLSOJ #12469 -「NOIP2021模拟赛1002北大附」蝴蝶图(暴力划分数+MST)
首先考虑 \(l+r=n+1\) 的情况。显然我们只需让 \(L,R\) 中的点分别连通即可,因此我们把两个端点在 \(L\) 中的边按边权从小到大排序然后跑 Kruskal,对于 \(R\) 也同理,然后把两个权值之和加起来即可。
接下来考虑 \(l+r>n+1\) 的情况。\(l+r=n+1\) 的情况强烈暗示此题与 MST 有关,因此我们肯定要尽量往 MST 的方向思考。在下文中方便起见设 \(C=L\cap R,A=L\text{\\}C,B=R\text{\\}C\)。那么直接按照 \(l+r=n+1\) 的情形跑两遍 MST 的方法就不可取了,因为这样会使 \(C\) 中的边的贡献被重复计算。而直接枚举 \(C\) 内部的连边情况则复杂度太高也不太能接受。不过注意到 \(C\) 内部连边的情况肯定是一棵森林,并且在确定 \(C\) 内部连通块情况后,计算将 \(C\) 与 \(A\)、\(C\) 与 \(B\) 连通的代价计算只与 \(C\) 内部连通块情况有关,而根据集合划分计数,\(11\) 个点连通情况只有 \(B_{11}\approx 6.7\times 10^5\) 种(实在不行可以跑一下 这题 的 std),因此一种方法是暴力枚举 \(C\) 中点的连通情况,然后对于 \(C\) 的每个子集 \(S\subseteqq C\),预处理 \(f_S\) 表示通过 \(C\) 内部的边将 \(S\) 中点连通的最小代价,这个就暴力 \(2^{11}\) 枚举子集然后 Kruskal 算一下即可,复杂度上界大概是 \(2^{11}·m\),不过数据大概没有特意卡所以这个复杂度也能过得去,这样对于一种连通块的情况,其需要的 \(C\) 内部边的代价就是所有连通块的 \(f\) 值之和。而对于 \(C\) 与 \(A\),\(C\) 与 \(B\) 连通的代价,则可以考虑将 \(C\) 中所有连通块分别缩成一个点,然后跑 MST 即可求得答案。
这样复杂度是 \(B_{11}\times m\times\alpha(m)\) 的,无法通过,考虑优化。注意到由于 \(C\) 大小很小,因此我们大胆猜测:有很多边,不论你 \(C\) 中连通情况如何,它都在最小生成树上。事实确实如此,以 \(L\) 中的点形成的最小生成树为例,我们考虑将 \(C\) 中所有点缩成一个大点并建一张只包含 \(A\) 中点和 \(C\) 缩成的大点的新图,那么我们如果再对这些边跑一遍 Kruskal,那么对于所有在缩点后的图的最小生成树上的边,肯定都有,不论你 \(C\) 连通情况如何,它们都在 \(L\) 的最小生成树上,这个感性理解一下即可。我们显然可以找出这些边组成的集合 \(E_L\),然后考虑找出 \(E_L\) 形成的图中所有连通块,显然这些连通块在后续的加边过程中肯定还是一个连通块,因此可以像 HNOI2010 城市建设 的套路那样,将它们缩成一个点,那么什么样的边,每次枚举 \(C\) 中连通情况都要重新检验一遍呢?显然原来不在 MST 上的边,肯定也不会在 MST 上,因此每次都要重新检验的边,只有在原图中 \(L\) 的最小生成树上,却不在 \(E_L\) 中的边。显然,原图中 \(L\) 的最小生成树边集大小为 \(|L|-1\),\(E_L\) 可以视作一个 \(|L|-|C|+1\) 个点的 MST 的边集,因此 \(|E_L|=|L|-|C|\),二者相减每次都要重新检验的边数等于 \(|C|-1\)。这个显然在我们能接受的范围内,因此我们缩点后直接将可能在最小生成树上的边按权值从小到大排序,再跑一次 MST 即可。时间复杂度也就是 \(B_{11}·11·\log 11+m\log m+2^{11}·m·\log m\)。
所以这道题你大概要跑 9 次 MST?这大概也就是此题实现起来异常毒瘤的原因吧。。。。。。
const int MAXN=1e5;
const int MAXM=2e5;
const int MAXK=22;
const int MAXP=2048;
int n,m,l,r,st[MAXN+5],p1,p2,k,id[MAXN+5];
int bel1[MAXN+5],bel2[MAXN+5],c1,c2;
vector<int> cap;
struct edge{
int u,v,w,on;
bool operator <(const edge &rhs){
return w<rhs.w;
}
} e[MAXM+5],ee[MAXM+5],nde1[MAXK+5],nde2[MAXK+5];
int f[MAXN+5],has[MAXN+5];ll sm[MAXP+5];
int find(int x){return (!f[x])?x:f[x]=find(f[x]);}
vector<int> comps;ll res=INFll;
void dfs(int lft){
if(!lft){
for(int i=1;i<=cap.size();i++) f[i]=0;
ll sum=0;int ed=0;
for(int x:comps) sum+=sm[x];
for(int x:comps){
int pos=-1;
for(int j=0;j<cap.size();j++) if(x>>j&1) pos=j;
for(int j=0;j<cap.size();j++)
if(x>>j&1){
int fu=find(bel1[cap[j]]),fv=find(bel1[cap[pos]]);
if(fu!=fv) f[fu]=fv,ed++;
}
}
for(int i=1;i<=p1;i++){
int fu=bel1[nde1[i].u],fv=bel1[nde1[i].v];
fu=find(fu);fv=find(fv);if(fu==fv) continue;
f[fu]=fv;sum+=nde1[i].w;ed++;
}
if(ed!=c1-1) return;
for(int i=1;i<=cap.size();i++) f[i]=0;
ed=0;
for(int x:comps){
int pos=-1;
for(int j=0;j<cap.size();j++) if(x>>j&1) pos=j;
for(int j=0;j<cap.size();j++)
if(x>>j&1){
int fu=find(bel2[cap[j]]),fv=find(bel2[cap[pos]]);
if(fu!=fv) f[fu]=fv,ed++;
}
}
for(int i=1;i<=p2;i++){
int fu=bel2[nde2[i].u],fv=bel2[nde2[i].v];
fu=find(fu);fv=find(fv);if(fu==fv) continue;
f[fu]=fv;sum+=nde2[i].w;ed++;
}
if(ed!=c2-1) return;
chkmin(res,sum);return;
} int lwb=lft&(-lft);
for(int i=lft;i;--i&=lft) if((i&lwb)==lwb){
comps.pb(i);dfs(lft^i);comps.ppb();
}
}
int main(){
// freopen("butterfly.in","r",stdin);
// freopen("butterfly.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&l,&r);
for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
for(int i=1,x;i<=l;i++) scanf("%d",&x),st[x]|=1;
for(int i=1,x;i<=r;i++) scanf("%d",&x),st[x]|=2;
for(int i=1;i<=m;i++) if(st[e[i].u]==3&&st[e[i].v]==3) ee[++k]=e[i];
sort(ee+1,ee+k+1);sort(e+1,e+m+1);
for(int i=1;i<=n;i++) if(st[i]==3) cap.pb(i),id[i]=cap.size();
for(int i=0;i<(1<<cap.size());i++){
int ed=0;
for(int j=1;j<=cap.size();j++) f[j]=0;
for(int j=1;j<=k;j++){
int idu=id[ee[j].u],idv=id[ee[j].v];
if((i>>(idu-1)&1)&&(i>>(idv-1)&1)){
idu=find(idu);idv=find(idv);
if(idu==idv) continue;ed++;
sm[i]+=ee[j].w;f[idu]=idv;
}
} if(ed!=__builtin_popcount(i)-1) sm[i]=1e14;
// printf("%d %lld\n",i,sm[i]);
} ll nd=0;
memset(f,0,sizeof(f));memset(has,0,sizeof(has));
for(int x:cap) has[x]=1;
for(int i=1;i<=m;i++){
if(st[e[i].u]==2||st[e[i].v]==2) continue;
if(st[e[i].u]==3&&st[e[i].v]==3) continue;
int fu=find(e[i].u),fv=find(e[i].v);
if(fu==fv) continue;
if(has[fu]&&has[fv]) nde1[++p1]=e[i];
else nd+=e[i].w,e[i].on=1;
f[fu]=fv;has[fv]|=has[fu];
}
memset(f,0,sizeof(f));memset(has,0,sizeof(has));
for(int x:cap) has[x]=1;
for(int i=1;i<=m;i++){
if(st[e[i].u]==1||st[e[i].v]==1) continue;
if(st[e[i].u]==3&&st[e[i].v]==3) continue;
int fu=find(e[i].u),fv=find(e[i].v);
if(fu==fv) continue;
if(has[fu]&&has[fv]) nde2[++p2]=e[i];
else nd+=e[i].w,e[i].on=2;
f[fu]=fv;has[fv]|=has[fu];
}
sort(nde1+1,nde1+p1+1);sort(nde2+1,nde2+p2+1);
// printf("%lld\n",nd);
memset(f,0,sizeof(f));memset(has,0,sizeof(has));
for(int i=1;i<=m;i++) if(e[i].on==1){
int fu=find(e[i].u),fv=find(e[i].v);
f[fu]=fv;
}
for(int i=1;i<=n;i++) if(st[i]==1||st[i]==3){
if(!bel1[find(i)]) bel1[find(i)]=++c1;
bel1[i]=bel1[find(i)];
}
memset(f,0,sizeof(f));memset(has,0,sizeof(has));
for(int i=1;i<=m;i++) if(e[i].on==2){
int fu=find(e[i].u),fv=find(e[i].v);
f[fu]=fv;
}
for(int i=1;i<=n;i++) if(st[i]==2||st[i]==3){
if(!bel2[find(i)]) bel2[find(i)]=++c2;
bel2[i]=bel2[find(i)];
} assert(c1<=cap.size());assert(c2<=cap.size());
memset(f,0,sizeof(f));dfs((1<<cap.size())-1);
printf("%lld\n",nd+res);
return 0;
}
/*
7 11 4 4
1 2 1
1 3 5
1 4 4
2 3 2
2 7 3
3 4 3
3 5 2
3 7 1
4 5 1
4 6 3
6 7 2
2 3 5 7
1 3 4 6
7 14 6 6
1 2 1000000000
1 3 1000000000
1 4 1000000000
1 5 1000000000
1 6 1000000000
7 2 1000000000
7 3 1000000000
7 4 1000000000
7 5 1000000000
7 6 1000000000
2 3 1
3 4 1
4 5 1
5 6 1
1 2 3 4 5 6
2 3 4 5 6 7
*/

浙公网安备 33010602011771号