码迷,mamicode.com
首页 > 其他好文 > 详细

[SDOI2019] 世界地图

时间:2020-06-11 10:35:27      阅读:57      评论:0      收藏:0      [点我收藏+]

标签:printf   res   def   级别   reg   err   lct   mes   turn   

题意:

给你一个$n\times m$的网格图,Q组询问,每次询问删掉第$[l_{i},r_{i}]$列的所有点后这张图的MST(最小生成树)。

$n\leq 100,m,Q\leq 10000$。

 

题解:

平时我们求最小生成树都是用Kruskal:把边排个序依次加入,用并查集合并。

但其实还有一种LCT的做法:不排序随便加边,每加到一条边$(u,v,w)$:

  • 如果u,v不连通,直接加;
  • 如果u,v连通且路径$u\rightarrow v$上最大边权$\leq w$,直接跳过;
  • 否则,加入$(u,v,w)$并删除路径$u\rightarrow v$上的最大权边。

那么我们考虑这道题,首先如果可以维护每个前后缀的MST,那么我们可以用上面那个做法合并两个MST。

但有一个问题:如果维护MST上的所有点那空间和时间都会爆炸。

注意到其实我们需要的只是第1列的点与第m列的点两两之间的边权最大值。那么我们可以把其他点缩到边上,建一个虚树。

考虑前后缀要维护什么东西,首先需要维护原图MST的边权之和,然后维护一棵将原图MST中所有关键点拿出来建的虚树。

虚树的每条边是原图MST中这两个关键点之间边权的最大值,点数是$O(n)$级别的。

(维护树其实就是维护边集,维护点集会让你看起来像一个nt)

考虑怎么合并,发现其实不用写LCT,可以把这两种做法混起来用:把两个虚树的边和新边扔到一起跑Kruskal,然后重新dfs一遍建虚树。

于是现在问题只剩下怎么维护前后缀了,发现维护时就是把$MST[1,i-1]$和$MST(i)$合并,那么每次把第i列的点作为关键点,套用上面的做法即可。

复杂度$O((m+Q)nlogn)$,写起来细节比较多。注意重复利用空间,不要随便建一棵树又不清空。

 

套路:

  • 求最小生成树的两种方式;
  • 维护和/最值且复杂度过大$\rightarrow$只保留关键点$\rightarrow$建虚树。

 

代码:

技术图片
#include<bits/stdc++.h>
#define maxn 205
#define maxm 20005
#define inf 0x7fffffff
#define ll long long
#define rint register int
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
ll hd[maxn*maxm],to[maxm],nxt[maxm];
ll n,m,cnt,mk[maxn*maxm],dep[maxn*maxm];
ll siz[maxn*maxm],F[maxn*maxm],cst[maxm]; 
unsigned int SA,SB,SC; int lim;
struct edge{
    ll u,v,w;
    bool operator<(const edge b){return w<b.w;}
}cr[maxn][maxm],li[maxn][maxm];
struct MST{
    ll sum;
    vector<edge> E;
}pre[maxm],suf[maxm],res,tp;
int L,R;

inline ll read(){
    ll x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c==-) f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-0;
    return x*f;
}

inline 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;
}

inline ll find(ll x){return (F[x]==x)?x:(F[x]=find(F[x]));}
inline ll id(ll x,ll y){return (x-1)*m+y;}

inline edge make_edge(ll u,ll v,ll w){
    edge ans;
    ans.u=u,ans.v=v,ans.w=w;
    return ans;
}
inline void make_tree(ll x,ll opt){
    tp.sum=0,tp.E.clear();
    for(rint i=1;i<=n;i++){
        if(i<n) tp.E.push_back(li[i][x]),tp.sum+=li[i][x].w;
        if(opt==1 && x!=1) tp.E.push_back(cr[i][x-1]),tp.sum+=cr[i][x-1].w;
        if(opt==-1 && x!=m) tp.E.push_back(cr[i][x]),tp.sum+=cr[i][x].w;
    }
}

inline void addedge(ll u,ll v,ll w){
    to[++cnt]=v,cst[cnt]=w,nxt[cnt]=hd[u],hd[u]=cnt;
    to[++cnt]=u,cst[cnt]=w,nxt[cnt]=hd[v],hd[v]=cnt;
}

inline void dfs(ll u,ll fa){
    siz[u]=mk[u];
    for(rint i=hd[u];i;i=nxt[i]){
        ll v=to[i],w=cst[i];
        if(v==fa) continue;
        dfs(v,u),siz[u]+=(siz[v]>0);
    }
    if(siz[u]>=2) mk[u]=1; 
}


inline void Dfs(ll u,ll fa,ll las,ll rt){
    if(mk[u] && u!=rt){
        res.E.push_back(make_edge(las,u,dep[u]));
        las=u,dep[u]=0;
    }
    for(rint i=hd[u];i;i=nxt[i]){
        ll v=to[i],w=cst[i];
        if(v==fa) continue;
        dep[v]=max(dep[u],w),Dfs(v,u,las,rt);
    }
}

inline MST merge(MST a,MST b,ll x,ll y){
    tp.sum=0,tp.E.clear(),res.E.clear(),cnt=0;
    for(rint i=0;i<a.E.size();i++) tp.E.push_back(a.E[i]),res.sum-=a.E[i].w;
    for(rint i=0;i<b.E.size();i++) tp.E.push_back(b.E[i]),res.sum-=b.E[i].w;
    sort(tp.E.begin(),tp.E.end());
    for(rint i=0;i<tp.E.size();i++){
        ll u=tp.E[i].u,v=tp.E[i].v;
        if(u%m==x%m || u%m==y%m) mk[u]=1; else mk[u]=0;
        if(v%m==x%m || v%m==y%m) mk[v]=1; else mk[v]=0;
        F[u]=u,F[v]=v,hd[u]=hd[v]=0;
    }
    for(rint i=0;i<tp.E.size();i++){
        ll u=tp.E[i].u,v=tp.E[i].v,w=tp.E[i].w;
        ll t1=find(u),t2=find(v);
        if(t1!=t2) F[t2]=t1,res.sum+=w,addedge(u,v,w);
    }
    dfs(x,0),dep[x]=0,Dfs(x,0,x,x);
    return res;
}

int main(){
    n=read(),m=read(),SA=read(),SB=read(),SC=read(),lim=read();
    for(rint i=1;i<=n;i++)
        for(rint j=1;j<=m;j++){
            if(j<m) cr[i][j]=make_edge(id(i,j),id(i,j+1),getweight());
            else cr[i][j]=make_edge(id(i,j),id(i,1),getweight());
        }
    for(rint i=1;i<n;i++)
        for(rint j=1;j<=m;j++)
            li[i][j]=make_edge(id(i,j),id(i+1,j),getweight());
    memset(mk,0,sizeof(mk));
    memset(hd,0,sizeof(hd));
    for(rint i=1;i<=n;i++) 
        for(rint j=1;j<=m;j++) 
            F[id(i,j)]=id(i,j);
    pre[0].sum=suf[m+1].sum=0;
    for(rint j=1;j<=m;j++)
        make_tree(j,1),res.sum=pre[j-1].sum+tp.sum,pre[j]=merge(pre[j-1],tp,1,j);
    memset(mk,0,sizeof(mk));
    memset(hd,0,sizeof(hd));
    for(rint i=1;i<=n;i++) 
        for(rint j=1;j<=m;j++) 
            F[id(i,j)]=id(i,j);
    for(rint j=m;j>=1;j--)
        make_tree(j,-1),res.sum=suf[j+1].sum+tp.sum,suf[j]=merge(suf[j+1],tp,m,j);
    memset(mk,0,sizeof(mk));
    memset(hd,0,sizeof(hd));
    for(rint i=1;i<=n;i++) 
        for(rint j=1;j<=m;j++) 
            F[id(i,j)]=id(i,j);
    ll q=read();
    while(q--){
        ll l=read()-1,r=read()+1; tp=suf[r];
        for(rint i=1;i<=n;i++) 
            tp.E.push_back(cr[i][m]),tp.sum+=cr[i][m].w;
        res.sum=pre[l].sum+tp.sum;
        merge(tp,pre[l],1,m);
        printf("%lld\n",res.sum);
    }
    return 0;
}
世界地图

 

[SDOI2019] 世界地图

标签:printf   res   def   级别   reg   err   lct   mes   turn   

原文地址:https://www.cnblogs.com/YSFAC/p/13091580.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!