SDOI2019 世界地图
删去一段区间,求剩下的东西的MST。很容易想到求前缀后缀然后合并,但是重点就是怎么求前缀MST,以及怎么合并。求前缀的过程其实也就是把上一列的前缀和这一列合并,所以问题就是怎么合并两棵最小生成树。
发现这个题两棵树之间只有\(2n\)个点之间有\(n\)条边相连。也就是说合并两个MST,只有他们中间合并的一面的那\(2n\)个点之间的树边可能会被砍掉。那么考虑记下\([1,x]\)这个区间的MST里,关于第一列和第\(x\)列这\(2n\)个点的虚树即可。实际上不用把虚树求出来,只需要把虚树上每条边上的最大权值那条边记下,每个前缀/后缀需要记住\(O(n)\)条边即可。而且发现只要连通性不改变,一条边的两个端点连什么其实无所谓。所以其实可以把一条边的两个端点变成所在的连通块的关键点。合并的时候就把两个MST的这\(O(n)\)条边都删掉,把权值减掉,再把这些边跑个最小生成树记住即可。好妙啊。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
#define N 1000002
typedef long long ll;
struct node{int x,y,len;};
unsigned int SA, SB, SC;int lim;
int n,m,va[102][10002],v2[102][10002],f[N],Q;
ll su1[10002],su2[10002];
bool vp[N];
int getroot(int x){return !f[x]?x:f[x]=getroot(f[x]);}
vector<node>s1[10002],s2[10002];
inline bool cmp(node n1,node n2){return n1.len<n2.len;}
inline int id(int x,int y){return (x-1)*m+y;}
int getweight(){
SA^=SA<<16;SA^=SA>>5;SA^=SA<<1;
unsigned int t=SA;
SA=SB;SB=SC;SC^=t^SA;
return SC%lim+1;
}
void gen() {
scanf("%d%d%u%u%u%d", &n, &m, &SA, &SB, &SC, &lim);
int i, j;
for(i = 1; i <= n; i++)
for(j = 1; j <= m; j++) {
if (j < m) va[i][j+1]=getweight();
else va[i][1]=getweight();
}
for(i = 1; i < n; i++)
for(j = 1; j <= m; j++) {
v2[i][j]=getweight();
}
}
inline ll solve(vector<node>&a,vector<node>&b)
{
sort(a.begin(),a.end(),cmp);b.clear();ll ret=0;
for(int i=0;i<a.size();i++)
{
int x=getroot(a[i].x),y=getroot(a[i].y);if(x==y)continue;
ret+=a[i].len;
if(vp[x]&&vp[y])f[x]=y,b.push_back((node){x,y,a[i].len});
else if(vp[x])f[y]=x;
else f[x]=y;
}return ret;
}
int main()
{gen();
for(int i=1;i<n;i++)s1[1].push_back((node){id(i,1),id(i+1,1),v2[i][1]});
for(int i=1;i<=n;i++)vp[id(i,1)]=1;
vector<node>v=s1[1];ll su=0;
for(int i=0;i<v.size();i++)su+=v[i].len;su1[1]=su;
for(int i=2;i<=m;i++)
{
for(int j=0;j<v.size();j++)su-=v[j].len;
for(int j=1;j<=n;j++)
{
v.push_back((node){id(j,i),id(j,i-1),va[j][i]});
f[id(j,i)]=0;vp[id(j,i)]=1;
f[id(j,i-1)]=0;if(i>2)vp[id(j,i-1)]=0;
f[id(j,1)]=0;
}
for(int j=1;j<n;j++)v.push_back((node){id(j,i),id(j+1,i),v2[j][i]});
vector<node>tv;su+=solve(v,tv);su1[i]=su;
s1[i]=tv;v=tv;
}
for(int i=1;i<n;i++)s2[m].push_back((node){id(i,m),id(i+1,m),v2[i][m]});
for(int i=1;i<=n;i++)vp[id(i,m)]=1;
v=s2[m];su=0;
for(int i=0;i<v.size();i++)su+=v[i].len;su2[m]=su;
for(int i=m-1;i;i--)
{
for(int j=0;j<v.size();j++)su-=v[j].len;
for(int j=1;j<=n;j++)
{
v.push_back((node){id(j,i),id(j,i+1),va[j][i+1]});
f[id(j,i)]=0;vp[id(j,i)]=1;
f[id(j,i+1)]=0;if(i<m-1)vp[id(j,i+1)]=0;
f[id(j,m)]=0;
}
for(int j=1;j<n;j++)v.push_back((node){id(j,i),id(j+1,i),v2[j][i]});
vector<node>tv;su+=solve(v,tv);su2[i]=su;
s2[i]=tv;v=tv;
}
scanf("%d",&Q);
while(Q--)
{
int l,r;scanf("%d%d",&l,&r);
ll ans=su1[l-1]+su2[r+1];
for(int i=0;i<s1[l-1].size();i++)ans-=s1[l-1][i].len;
for(int i=0;i<s2[r+1].size();i++)ans-=s2[r+1][i].len;
vector<node>tv;v.clear();
for(int i=0;i<s1[l-1].size();i++)v.push_back(s1[l-1][i]);
for(int i=0;i<s2[r+1].size();i++)v.push_back(s2[r+1][i]);
for(int i=1;i<=n;i++)v.push_back((node){id(i,1),id(i,m),va[i][1]});
for(int i=1;i<=n;i++)f[id(i,1)]=f[id(i,m)]=0,vp[id(i,1)]=vp[id(i,m)]=1,
f[id(i,l-1)]=f[id(i,r+1)]=0,vp[id(i,l-1)]=vp[id(i,r+1)]=1;
ans+=solve(v,tv);printf("%lld\n",ans);
}
}