【題解】Luogu P5360 [SDOI2019]世界地圖

原題傳送門

每次查詢的實際就是將地圖的一個前綴和一個後綴合併後的圖的最小生成樹邊權和

咱們要預處理每一個前綴和後綴的最小生成樹

實際求前綴和(後綴和)的過程珂以理解爲上一個前綴和這一列的最小生成樹進行合併,實際最後前綴和後綴合併也是這樣

若是暴力進行合併的話,每次邊數是nm級別的,明顯會TLE和MLE

咱們考慮一下,實際每次合併主要和最左、最右兩列(稱這些點爲關鍵點)有關,每次合併,原來最小生樹中有可能會有一些邊要刪掉使得合併後是最小生成樹。感性理解一下,珂能刪掉的邊必定在兩個關鍵點在原來最小生成樹之間的鏈上,因此咱們對關鍵點建最小生成樹的虛樹,邊權爲原來最小生成樹之間兩點邊權的最大值,其餘的邊權累加成和便可(由於其餘的邊不珂能刪掉),這時兩個最小生成樹的邊數的數量級都是n*常數的,因此直接暴力kruscal。

這個算法的複雜度大概爲\(O(n*(m+q)\log n)\)

#include <bits/stdc++.h>
#define N 10005 
#define ll long long
using namespace std;
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register ll x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
inline int Max(register int a,register int b)
{
    return a>b?a:b;
}
int n,m,q,foo[N][105],bar[N][105];
unsigned int SA,SB,SC;
int lim;
inline int rng()
{
    SA^=SA<<16;SA^=SA>>5;SA^=SA<<1;
    unsigned int t=SA;SA=SB;SB=SC;SC^=t^SA;
    return SC%lim+1;
}
struct edge{
    int u,v,w;
    bool operator < (const edge &b)const{return w<b.w;}
};
struct MST{
    int tot;
    ll sum;
    vector <edge> E;
    MST(){}
    MST(register int *c)
    {
        tot=n,sum=0;
        for(register int i=1;i<n;++i)
            E.push_back((edge){i,i+1,c[i]});
    }
    inline ll query()
    {
        ll res=sum;
        for(register int i=0;i<E.size();++i)
            res+=E[i].w;
        return res;
    }
}pre[N],suf[N];
int tot,fa[N],mrk[N],to[N],nxt[N],ww[N],head[N],cnt;
vector <edge> E;
ll ans;
inline int find(register int x)
{
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline void link(register edge x)
{
    to[++cnt]=x.v,nxt[cnt]=head[x.u],ww[cnt]=x.w,head[x.u]=cnt;
    to[++cnt]=x.u,nxt[cnt]=head[x.v],ww[cnt]=x.w,head[x.v]=cnt;
    ans+=x.w;
}
inline bool dfs1(register int u,register int f)
{
    int s=0;
    for(register int e=head[u];e;e=nxt[e])
        if(to[e]!=f)
            s+=dfs1(to[e],u);
    mrk[u]|=(s>=2);
    s+=mrk[u];
    return s;   
}
inline void dfs2(register int u,register int f,register int lst,register int val)
{
    if(mrk[u])
    {
        if(lst)
            E.push_back((edge){mrk[u],lst,val});
        lst=mrk[u];
        ans-=val;
        val=0;
    }
    for(register int e=head[u];e;e=nxt[e])
        if(to[e]!=f)
            dfs2(to[e],u,lst,Max(val,ww[e]));
}
inline MST merge(register MST a,register MST b,register int *c)
{
    tot=a.tot+b.tot;
    E.clear();
    for(register int i=0;i<a.E.size();++i)
        E.push_back(a.E[i]);
    for(register int i=0;i<b.E.size();++i)
        E.push_back((edge){b.E[i].u+a.tot,b.E[i].v+a.tot,b.E[i].w});
    for(register int i=1;i<=n;++i)
        E.push_back((edge){a.tot-n+i,a.tot+i,c[i]});
    sort(E.begin(),E.end());
    for(register int i=1;i<=tot;++i)
        fa[i]=i,mrk[i]=(i<=n||i>tot-n),head[i]=0;
    cnt=ans=0;
    for(register int i=0;i<E.size();++i)
    {
        edge x=E[i];
        if(find(x.u)!=find(x.v))
            link(x),fa[find(x.u)]=find(x.v);
    }
    dfs1(1,0);
    cnt=0;
    for(register int i=1;i<=tot;++i)
        if(mrk[i])
            mrk[i]=++cnt;
    E.clear();
    dfs2(1,0,0,0);
    MST res;
    res.tot=cnt;
    res.sum=a.sum+b.sum+ans;
    res.E=E;
    return res;
}
int main()
{
    n=read(),m=read();
    scanf("%u%u%u",&SA,&SB,&SC);
    lim=read();
    for(register int i=1;i<=n;++i)
        for(register int j=1;j<=m;++j)
            foo[j][i]=rng();
    for(register int i=1;i<n;++i)
        for(register int j=1;j<=m;++j)
            bar[j][i]=rng();
    pre[1]=MST(bar[1]),suf[m]=MST(bar[m]);
    for(register int i=2;i<m;++i)
        pre[i]=merge(pre[i-1],MST(bar[i]),foo[i-1]);
    for(register int i=m-1;i>1;--i)
        suf[i]=merge(MST(bar[i]),suf[i+1],foo[i]);
    q=read();
    while(q--)
    {
        int l=read(),r=read();
        write(merge(suf[r+1],pre[l-1],foo[m]).query()),puts("");
    }
    return 0;
}
相關文章
相關標籤/搜索