[DS记录]P5360 [SDOI2019]世界地图

· · 个人记录

题意 : 一个左右相接的 n\times m 网格图,边有边权。

每次挖掉一个区间的列之后,求剩余部分的 \rm MST 边权和。

------------ 先预处理前缀生成树和后缀生成树,然后想办法合并。合并时会加入连接列 $1,n$ 的一列边。 对第一列和最后一列的点在生成树上建立虚树,每条虚树边中只会有一条最大的边可能被断开。 我们只需要维护端点虚树的最大边集合。 类似地,每加入一列边时,也只有最大边集合中的边可能被断开。 我们将图简化成只有虚树,和新加入的边一起跑 $\rm Kruskal$ 。将剩余的边形成的结构化简为虚树(缩二度点)。 当询问时,也只需要合并两组虚树的边。 一次合并的复杂度为 $O(n\log n)$ ,总复杂度为 $O((m+q)n\log n)$。 若使用归并排序,可以做到 $O(n\alpha(n))$ 合并。 大常数代码 : ```cpp #include<algorithm> #include<cstdio> #include<vector> #define pb push_back #define ll long long #define uint unsigned int #define MaxN 10050 #define MaxM 105 #define ll long long using namespace std; vector<int> g[MaxN*MaxM],l[MaxN*MaxM]; int st[MaxN<<4],tn; void adl(int u,int v,int w){ g[u].pb(v);l[u].pb(w); g[v].pb(u);l[v].pb(w); } void clrg() { for (int i=1;i<=tn;i++){ g[st[i]].clear(); l[st[i]].clear(); }tn=0; } ll tsum; struct Line{int u,v,w;}; vector<Line> sl2; bool side[MaxN*MaxM],siz[MaxN*MaxM]; int down[MaxN*MaxM],len[MaxN*MaxM]; void dfs(int u,int fa) { st[++tn]=u; int cnt=2*(siz[u]=side[u]); down[u]=u;len[u]=0; for (int i=0,v;i<g[u].size();i++) if ((v=g[u][i])!=fa){ dfs(v,u); cnt+=siz[v]; siz[u]|=siz[v]; } bool fl=(cnt>1||!fa); for (int i=0,v;i<g[u].size();i++) if ((v=g[u][i])!=fa){ if (siz[v]){ if (fl){ if (len[v]>l[u][i]) {sl2.pb((Line){u,down[v],len[v]});tsum+=l[u][i];} else {sl2.pb((Line){u,down[v],l[u][i]});tsum+=len[v];} }else { down[u]=down[v]; len[u]=max(len[v],l[u][i]); tsum+=min(len[v],l[u][i]); } }else tsum+=len[v]+l[u][i]; } } bool cmpL(const Line &A,const Line &B) {return A.w<B.w;} struct VMST { vector<Line> s; ll sum; void smpfy() { for (int i=0;i<s.size();i++) adl(s[i].u,s[i].v,s[i].w); tsum=0;sl2.clear(); dfs(s[0].u,0); sum+=tsum;s=sl2; clrg(); } ll calc(){ ll ret=0; for (int i=0;i<s.size();i++) ret+=s[i].w; return ret; } }tl[MaxN],tr[MaxN]; uint SA,SB,SC;int lim; int getweight() { SA^=SA<<16;SA^=SA>>5;SA^=SA<<1; uint t=SA; SA=SB;SB=SC;SC^=t^SA; return SC%lim+1; } int n,m,l0[MaxN][MaxM],l1[MaxN][MaxM]; void gen(){ scanf("%d%d%u%u%u%d",&m,&n,&SA,&SB,&SC,&lim); for(int j=1;j<=m;j++) for(int i=1;i<=n;i++) l0[i][j]=getweight(); for(int j=1;j<m;j++) for(int i=1;i<=n;i++) l1[i][j]=getweight(); } int f[MaxN*MaxM]; void clrf(){ for (int i=1;i<=tn;i++) f[st[i]]=st[i]; tn=0; } int find(int u) {return f[u]==u ? u : f[u]=find(f[u]);} bool merge(int u,int v){ u=find(u);v=find(v); if (u==v)return 0; f[u]=v;return 1; } void merge(VMST &T,vector<Line> &s) { for (int i=0;i<T.s.size();i++)s.pb(T.s[i]); sort(s.begin(),s.end(),cmpL); T.s.clear(); for (int i=0;i<s.size();i++) if (merge(s[i].u,s[i].v)){ T.s.pb(s[i]); st[++tn]=s[i].u; st[++tn]=s[i].v; } clrf(); } vector<Line> sl; #define tp(i,j) ((i-1)*m+j) int main() { gen(); for (int i=1;i<=n*m;i++)f[i]=i; for (int j=1;j<=m;j++)side[tp(1,j)]=side[tp(n,j)]=1; for (int j=1;j<m;j++) tl[1].s.pb((Line){tp(1,j),tp(1,j+1),l1[1][j]}); for (int i=2;i<n;i++){ sl.clear(); for (int j=1;j<m;j++)sl.pb((Line){tp(i,j),tp(i,j+1),l1[i][j]}); for (int j=1;j<=m;j++)sl.pb((Line){tp(i-1,j),tp(i,j),l0[i-1][j]}); for (int j=1;j<=m;j++)side[tp(i,j)]=1; tl[i]=tl[i-1];merge(tl[i],sl);tl[i].smpfy(); for (int j=1;j<=m;j++)side[tp(i,j)]=0; } for (int j=1;j<m;j++) tr[n].s.pb((Line){tp(n,j),tp(n,j+1),l1[n][j]}); for (int i=n-1;i>1;i--){ sl.clear(); for (int j=1;j<m;j++)sl.pb((Line){tp(i,j),tp(i,j+1),l1[i][j]}); for (int j=1;j<=m;j++)sl.pb((Line){tp(i,j),tp(i+1,j),l0[i][j]}); for (int j=1;j<=m;j++)side[tp(i,j)]=1; tr[i]=tr[i+1];merge(tr[i],sl);tr[i].smpfy(); for (int j=1;j<=m;j++)side[tp(i,j)]=0; } int q;scanf("%d",&q); while(q--){ int l,r;scanf("%d%d",&l,&r);l--;r++; VMST T=tl[l];sl=tr[r].s; for (int j=1;j<=m;j++)sl.pb((Line){tp(n,j),tp(1,j),l0[n][j]}); merge(T,sl); printf("%lld\n",tl[l].sum+tr[r].sum+T.calc()); } } ```