有点牛的题。
一个可能比较前置的技巧是 LCT 维护 MST 的方法,具体来说就是加边的时候,如果两边原本就是连通的,那么就把路径上的最大边权拿出来和要加的边进行比较,选择更优的那一个。这个技巧启示我们,在 MST 中只有任意两点的路径的最大边权是重要的,并且两张图的 MST 是支持进行合并的。
所以说,一个基本的思路是预处理出所有前缀和后缀对应的 MST 然后在查询的时候进行合并,但是总的点数是 \(n^2m\) 的,显然无法通过。
但是同时我们还注意到一个性质:这张图是一个网格图。这也就意味着,一个前缀/后缀形成的最小生成树,第 \(i\) 列和第 \(i+2\) 列的点是完全没有连边的。所以对于前缀/后缀的最小生成树,只有第一列和最后一列的点是重要的,那这不就是虚树吗?于是有一个朴素的做法,大力维护每个前缀/后缀的虚树然后在最后进行合并就可以了。
但是事实上这个虚树是不用真正建出来的,有一种基于并查集的更加方便的做法。具体来说,我们假设已经知道了前 \(i-1\) 列形成的 MST,我们现在要加到第 \(i\) 列,显然我们应该直接把原本两棵 MST 的边与连接两边的边一起做 Kruskal,但是为了控制点数,在这个过程中我们不希望第 \(i-1\) 列的点出现在新的 MST 中,所以并查集合并的时候,如果有一个集合里没有任何一个第一列或第 \(m\) 列的点,那么他就是不需要真正加入新 MST 中的边。但是它已经确定加入了贡献里且不再会被踢出,所以我们需要把它的边权累加进答案,然后把父亲指向有第一列或第 \(m\) 列的点的集合。否则,可以证明直接连接两个集合的祖先节点是等价的,然后把这条边加入新的边集即可。
具体来说,因为 MST 中只关心任意两点的路径的最大边权,且在 Kruskal 的过程中这条边一定是最大的,所以在两个集合中无论怎么连都能够保证路径的最大边权,所以起到的效果是等价的。
所以我们可以直接用并查集维护前后缀的 MST,询问时暴力合并即可 。
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
namespace IN{
const int Q = 1000000;
#define getc() (p1==p2&&(p2=(p1=buf)+inbuf->sgetn(buf,Q),p1==p2)?EOF:*p1++)
char buf[Q],*p1,*p2;
template<typename T>inline bool read(T &x) {
static std::streambuf *inbuf=cin.rdbuf();
x=0;
register int f=0,flag=false;
register char ch=getc();
while(!isdigit(ch)){
if (ch=='-') f=1;
ch=getc();
}
if(isdigit(ch)) x=x*10+ch-'0',ch=getc(),flag=true;
while(isdigit(ch)) {
x=x*10+ch-48;
ch=getc();
}
x=f?-x:x;
return flag;
}
template<typename T,typename ...Args>inline bool read(T& a,Args& ...args) {
return read(a)&&read(args...);
}
#undef getc
}
using namespace IN;
const int N=105,M=10005;
int n,m,q,r[N][M],c[N][M];
unsigned int SA, SB, SC;int lim;
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, w;
for(i = 1; i <= n; i++)
for(j = 1; j <= m; j++) {
// w = getweight();
// if (j < m) {
// addedge(i, j, i, j + 1, w);
// } else {
// addedge(i, j, i, 1, w);
// }
r[i][j]=getweight();
}
for(i = 1; i < n; i++)
for(j = 1; j <= m; j++) {
// w = getweight();
c[i][j]=getweight();
// addedge(i, j, i + 1, j, w);
}
}
struct edge{
int u,v,w;
};
inline int getid(int x,int y){
return (x-1)*m+y;
}
struct MST{
long long sum;
vector<edge>vec;
MST(){sum=0;}
MST(int id){
sum=0;
for(signed i=1;i<n;++i)vec.push_back({getid(i,id),getid(i+1,id),c[i][id]});
}
void print(){
cout<<"CHECK\n";
cout<<"SUM = "<<sum<<'\n';
for(auto x:vec)cout<<x.u<<' '<<x.v<<' '<<x.w<<'\n';
}
}pre[M],suf[M];
int fa[N*M];
bool flg[N*M];
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
bool cmp(edge x,edge y){
return x.w<y.w;
}
MST merge(int id,bool opt){
MST ans;
ans.vec.clear();
vector<edge>vec;
for(signed i=1;i<=n;++i)fa[getid(i,id)]=getid(i,id),flg[getid(i,id)]=1;
if(!opt){
for(signed i=1;i<=n;++i){
fa[getid(i,id-1)]=getid(i,id-1);
flg[getid(i,id-1)]=0;
fa[getid(i,1)]=getid(i,1);
flg[getid(i,1)]=1;
}
}
else{
for(signed i=1;i<=n;++i){
fa[getid(i,id+1)]=getid(i,id+1);
flg[getid(i,id+1)]=0;
fa[getid(i,m)]=getid(i,m);
flg[getid(i,m)]=1;
}
}
if(!opt)for(auto x:pre[id-1].vec)vec.push_back(x);
else for(auto x:suf[id+1].vec)vec.push_back(x);
for(signed i=1;i<n;++i)vec.push_back({getid(i,id),getid(i+1,id),c[i][id]});
if(!opt)for(signed i=1;i<=n;++i)vec.push_back({getid(i,id-1),getid(i,id),r[i][id-1]});
else for(signed i=1;i<=n;++i)vec.push_back({getid(i,id),getid(i,id+1),r[i][id]});
sort(vec.begin(),vec.end(),cmp);
if(!opt)ans.sum=pre[id-1].sum;
else ans.sum=suf[id+1].sum;
for(auto x:vec){
int a=find(x.u),b=find(x.v),c=x.w;
if(a==b)continue;
if(!flg[a]||!flg[b])ans.sum+=c;
if(!flg[a])fa[a]=b;
else if(!flg[b])fa[b]=a;
else fa[b]=a,ans.vec.push_back({a,b,c});
}
return ans;
}
long long solve(int l,int r){
//do sth.
for(signed i=1;i<=n;++i)fa[getid(i,1)]=getid(i,1),fa[getid(i,l-1)]=getid(i,l-1),fa[getid(i,r+1)]=getid(i,r+1),fa[getid(i,m)]=getid(i,m);
vector<edge>vec;
for(auto x:pre[l-1].vec)vec.push_back(x);
for(auto x:suf[r+1].vec)vec.push_back(x);
for(signed i=1;i<=n;++i)vec.push_back({getid(i,1),getid(i,m),::r[i][m]});
// for(auto x:vec)cout<<x.u<<' '<<x.v<<' '<<x.w<<'\n';
long long ans=pre[l-1].sum+suf[r+1].sum;
// cout<<ans<<'\n';
sort(vec.begin(),vec.end(),cmp);
for(auto x:vec){
int a=find(x.u),b=find(x.v),c=x.w;
if(a==b)continue;
fa[a]=b;
ans+=c;
// cout<<x.u<<' '<<x.v<<' '<<c<<'\n';
}
return ans;
}
signed main()
{
// freopen("ds.in","r",stdin);
// freopen("ds.out","w",stdout);
// cin>>n>>m>>op>>lim>>seed;
gen();
pre[1]=MST(1);
suf[m]=MST(m);
for(signed i=2;i<=m;++i)pre[i]=merge(i,0);
// for(signed i=1;i<=m;++i)pre[i].print();
for(signed i=m-1;i>=1;--i)suf[i]=merge(i,1);
// do sth.
// q=read();
read(q);
while(q--){
int l,r;
read(l);read(r);
// =read(),r=read();
printf("%lld\n",solve(l,r));
}
return 0;
}
标签:ch,read,MST,int,P5360,vec,getc
From: https://www.cnblogs.com/Forever1507/p/18662898