首页 > 其他分享 >P5360

P5360

时间:2025-01-09 21:11:42浏览次数:1  
标签:ch read MST int P5360 vec getc

有点牛的题。

一个可能比较前置的技巧是 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

相关文章