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);
    }
}
posted @ 2021-03-16 16:46  Lebron_Durant  阅读(138)  评论(0)    收藏  举报