NFLSOJ #12448. 「NOIP2021模拟赛p_b_p_b#2」子图(并查集)
模拟赛又被送走了,来写篇题解。
首先考虑“极大”这个条件有什么性质。不难发现如果 \(V_1,V_2\) 为点集的导出子图符合条件,且 \(V_1\cap V_2\ne\varnothing\),那 \(V_1\cup V_2\) 一定也是符合条件的连通导出子图。也就是说极大的导出子图一定不能有交,它们一定可以表示为一张图中某些连通块的形式。
我们考虑正序枚举 \(k\)。那么随着 \(k\) 的增长,符合条件的极大导出子图肯定是在不断变小/分裂的过程。具体来说,我们假设 \(k-1\) 时刻极大导出子图为 \(G_{k-1}\) 的所有连通块,那么我们考虑这样的过程:每次删除 \(G_{k-1}\) 中所有度 \(<k\) 的点及所有与其相连的边,这样还会导致更多的点度数变得 \(<k\),我们就一直重复这个操作直到操作无法进行下去即可。不难发现经过这些操作之后该删的点都删了,剩下的点自然就是我们要求的 \(G_k\) 了。我们对 \(G_k\) 每个连通块都计算一遍答案即可。
暴力 \(n^2\) 地求肯定无法通过。不过注意到每个点只会被删一次,也就是说每个连通块在多数情况下,其形态是不会发生改变的,而我们要在权值最大的情况下找出最大的 \(k\),也就是说只有这个连通块被破坏地前一瞬间,它的答案才是我们真正关心的。删点不好处理,考虑将删点过程离线下来变成加点过程。那么连通块被破坏前的一瞬间,就变为了,连通块形成的那一瞬间,也就是说我们只用对每个时刻,新加入的点所在的连通块计算贡献即可。
还有就是怎样维护一个点的答案。\(n,m\) 维护起来较容易不再赘述,至于 \(b\),不难发现 \(\sum\limits_{x\in S}deg_x=b+2m\),其中 \(S\) 为连通块,\(deg_x\) 为 \(x\) 在原图中的度,因此我们再维护一个连通块中所有点在原图中的度数之和即可。时间复杂度 \(n\log n\)。
const int MAXN=1e6;
const ll INF=0x3f3f3f3f3f3f3f3fll;
int n,m,M,N,B,deg[MAXN+5],hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int f[MAXN+5],V[MAXN+5],E[MAXN+5],D[MAXN+5];
int find(int x){return (!f[x])?x:f[x]=find(f[x]);}
void merge(int x,int y){
x=find(x);y=find(y);E[x]++;if(x==y) return;
f[x]=y;E[y]+=E[x];D[y]+=D[x];V[y]+=V[x];
}
vector<int> del[MAXN+5];bool vis[MAXN+5];
pair<ll,int> res=mp(-INF,0);
ll calc(int x){x=find(x);return -1ll*N*V[x]+1ll*M*E[x]+1ll*B*(D[x]-(E[x]<<1));}
int main(){
freopen("subgraph.in","r",stdin);
freopen("subgraph.out","w",stdout);
scanf("%d%d%d%d%d",&n,&m,&M,&N,&B);
for(int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u),deg[u]++,deg[v]++;
for(int i=1;i<=n;i++) V[i]=1,D[i]=deg[i];
multiset<pii> st;
for(int i=1;i<=n;i++) st.insert(mp(deg[i],i));
for(int i=0;i<n;i++){
while(!st.empty()&&(*st.begin()).fi<=i){
pii p=*st.begin();st.erase(st.find(p));
int x=p.se;del[i].pb(x);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];
if(st.find(mp(deg[y],y))!=st.end()){
st.erase(st.find(mp(deg[y],y)));
deg[y]--;st.insert(mp(deg[y],y));
}
}
}
}
for(int i=n-1;i;i--){
for(int x:del[i]){
vis[x]=1;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];
if(vis[y]) merge(x,y);
}
}
for(int x:del[i]) chkmax(res,mp(calc(x),i));
} printf("%d %lld\n",res.se,res.fi);
return 0;
}

浙公网安备 33010602011771号