zoukankan      html  css  js  c++  java
  • [SDOI2019]世界地图(kruskal重构树+虚树)

    通过子任务1、3十分显然,子任务4实际上就是线段树,和我下午写的[SDOI2015]道路修建一模一样,堪称“我抄我自己”,不会的可以先做一下这个题。

    然后考虑正解,参考了zhoushuyu的博客,首先可以对前i列做MST,就是把前i-1列和第i列合并起来,而这时候只需要把第1和第i列的点作为关键点建立虚树,虚树边权为原树路径最大值,然后每次O(n)对虚树合并即可。后缀也同样做一遍即可。查询时,就是把整张图分成两半,同样只需要维护前后缀的左右两列建立虚树即可,复杂度O(n(m+q)logn)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=10086;
    struct edge{int u,v,w;};
    bool operator<(edge a,edge b){return a.w<b.w;}
    int n,m,Q,lim,tot,cnt,d1[N][110],d2[N][110],fa[N],hd[N],v[N],nxt[N],w[N],vis[N];
    ll ans;
    unsigned int SA,SB,SC;
    vector<edge>G;
    struct MST{
        int tot;ll sum;
        vector<edge>G;
        MST(){}
        MST(int*c)
        {
            tot=n,sum=0;
            for(int i=1;i<n;i++)G.push_back((edge){i,i+1,c[i]});
        }
    }pre[N],suf[N];
    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;
    }
    int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
    void adde(edge x)
    {
        v[++cnt]=x.v,nxt[cnt]=hd[x.u],w[cnt]=x.w,hd[x.u]=cnt;
        v[++cnt]=x.u,nxt[cnt]=hd[x.v],w[cnt]=x.w,hd[x.v]=cnt;
        ans+=x.w;
    }
    bool dfs1(int u,int f)
    {
        int ret=0;
        for(int i=hd[u];i;i=nxt[i])if(v[i]!=f)ret+=dfs1(v[i],u);
        vis[u]|=(ret>=2),ret+=vis[u];
        return ret;
    }
    void dfs2(int u,int f,int lst,int val)
    {
        if(vis[u])
        {
            if(lst)G.push_back((edge){vis[u],lst,val});
            lst=vis[u],ans-=val,val=0;
        }
        for(int i=hd[u];i;i=nxt[i])if(v[i]!=f)dfs2(v[i],u,lst,max(val,w[i]));
    }
    MST merge(MST a,MST b,int*c)
    {
        G.clear(),tot=a.tot+b.tot;
        for(int i=0;i<a.G.size();i++)G.push_back(a.G[i]);
        for(int i=0;i<b.G.size();i++)G.push_back((edge){b.G[i].u+a.tot,b.G[i].v+a.tot,b.G[i].w});
        for(int i=1;i<=n;i++)G.push_back((edge){a.tot-n+i,a.tot+i,c[i]});
        sort(G.begin(),G.end());
        for(int i=1;i<=tot;i++)fa[i]=i,vis[i]=(i<=n||i>tot-n),hd[i]=0;
        ans=cnt=0;
        for(int i=0;i<G.size();i++)
        {
            int u=find(G[i].u),v=find(G[i].v);
            if(u!=v)adde(G[i]),fa[u]=v;
        }
        dfs1(1,0),cnt=0;
        for(int i=1;i<=tot;i++)if(vis[i])vis[i]=++cnt;
        G.clear(),dfs2(1,0,0,0);
        MST ret;ret.tot=cnt,ret.sum=a.sum+b.sum+ans,ret.G=G;
        return ret;
    }
    ll query(MST a)
    {
        ll ret=a.sum;
        for(int i=0;i<a.G.size();i++)ret+=a.G[i].w;
        return ret;
    }
    int main()
    {
        scanf("%d%d%u%u%u%d",&n,&m,&SA,&SB,&SC,&lim);
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)d1[j][i]=getweight();
        for(int i=1;i<n;i++)for(int j=1;j<=m;j++)d2[j][i]=getweight();
        pre[1]=MST(d2[1]),suf[m]=MST(d2[m]);
        for(int i=2;i<m;i++)pre[i]=merge(pre[i-1],MST(d2[i]),d1[i-1]);
        for(int i=m-1;i>1;i--)suf[i]=merge(MST(d2[i]),suf[i+1],d1[i]);
        scanf("%d",&Q);
        while(Q--)
        {
            int l,r;scanf("%d%d",&l,&r);
            printf("%lld
    ",query(merge(suf[r+1],pre[l-1],d1[m])));
        }
    }
    View Code
  • 相关阅读:
    python+selenium之页面元素截图
    selenium八大定位
    http概述之URL与资源
    数组中只出现一次的数字
    数字在排序数组中出现的次数
    把数组排成最小的数
    数组中出现次数超过一半的数字
    调整数组顺序使得奇数位于偶数的前面
    旋转数组的最小值
    二维数组的查找
  • 原文地址:https://www.cnblogs.com/hfctf0210/p/10828722.html
Copyright © 2011-2022 走看看