Gym101889 - I Imperial roads(最小生成树,树链剖分)

链接:Gym101889

题意:

给出 R R 条带权边,构建一棵 N ( 2 N 1 0 5 ) N\,(2\le N\le 10^5) 个结点的最小生成树,共 Q ( 1 Q 1 0 5 ) Q\,(1\le Q\le 10^5) 次询问,每次询问必选边 ( U , V ) (U,V) 的最小生成树的边权和。



分析:

先不考虑必选边,建一棵最小生成树,对于询问必选边 ( U , V ) (U,V) ,找到最小生成树中 U U , V V 路径上最大边权,然后删除该边,将边 ( U , V ) (U,V) 加上即可。

对于找到树上 U U , V V 路径上的最大边权,将 边权 赋到 该边更深结点点权上,然后用树链剖分即可。



代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int INF=0x3f3f3f3f;
const int maxn=1e6+10;
int N,R,Q,min_sum=0;
struct road
{
    int u;
    int v;
    int w;
    bool friend operator < (const road &x,const road &y)
    {
        return x.w>y.w;
    }
}r[maxn];
struct edge
{
    int u;
    int v;
    int w;
    int next;
}e[maxn];
int head[maxn],cnt;
void add_edge(int u,int v,int w)
{
    e[cnt]=edge{u,v,w,head[u]};
    head[u]=cnt++;
    e[cnt]=edge{v,u,w,head[v]};
    head[v]=cnt++;
}
map<PII,int> cost;
int father[maxn];
int find_father(int x)
{
    int root=x;
    while(father[root]!=root)
        root=father[root];
    while(father[x]!=x)
    {
        int t=x;
        x=father[x];
        father[t]=root;
    }
    return root;
}
void kruskal()
{
    priority_queue<road> q;
    for(int i=1;i<=R;i++)
        q.push(r[i]);
    while(!q.empty())
    {
        int u=q.top().u,v=q.top().v,w=q.top().w;
        q.pop();
        int Fu=find_father(u),Fv=find_father(v);
        if(Fu!=Fv)
        {
            father[Fu]=Fv;
            add_edge(u,v,w);
            min_sum+=w;
        }
    }
}
int fa[maxn],dep[maxn],sz[maxn],son[maxn];
void dfs1(int u,int pre,int depth)
{
    fa[u]=pre;
    dep[u]=depth;
    sz[u]=1;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v;
        if(v==pre)
            continue;
        dfs1(v,u,depth+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])
            son[u]=v;
    }
}
int id[maxn],top[maxn],wt[maxn],tot=0;
void dfs2(int u,int TOP)
{
    top[u]=TOP;
    id[u]=++tot;
    if(!son[u])
        return;
    dfs2(son[u],TOP);
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v;
        if(v!=son[u]&&v!=fa[u])
            dfs2(v,v);
    }
}
#define ls rt<<1
#define rs rt<<1|1
int t[maxn<<2];
void build(int rt,int l,int r)
{
    if(l==r)
    {
        t[rt]=wt[l];
        return;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    t[rt]=max(t[ls],t[rs]);
}
int query(int rt,int l,int r,int ql,int qr)
{
    if(ql<=l&&r<=qr)
        return t[rt];
    int mid=(l+r)>>1;
    int ans=0;
    if(ql<=mid)
        ans=max(ans,query(ls,l,mid,ql,qr));
    if(qr>mid)
        ans=max(ans,query(rs,mid+1,r,ql,qr));
    return ans;
}
int tree_query(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>=dep[top[y]])
        {
            ans=max(ans,query(1,1,N,id[top[x]],id[x]));
            x=fa[top[x]];
        }
        else
        {
            ans=max(ans,query(1,1,N,id[top[y]],id[y]));
            y=fa[top[y]];
        }
    }
    if(x==y)
        return ans;
    if(id[x]<id[y])
        ans=max(ans,query(1,1,N,id[son[x]],id[y]));    //因为把边权给了该边更深点的点权
    else                                               //所以同一条重链上的边是从son[x]到y
        ans=max(ans,query(1,1,N,id[son[y]],id[x]));
    return ans;
}
void init()
{
    memset(head,-1,sizeof(head));
    cnt=0;
    for(int i=1;i<=N;i++)
        father[i]=i;
}
int main()
{
    scanf("%d %d",&N,&R);
    for(int i=1;i<=R;i++)
    {
        scanf("%d %d %d",&r[i].u,&r[i].v,&r[i].w);
        if(r[i].u>r[i].v)
            swap(r[i].u,r[i].v);
        cost[make_pair(r[i].u,r[i].v)]=r[i].w;
    }
    init();
    kruskal();
    dfs1(1,-1,0);
    dfs2(1,1);
    for(int i=0;i<cnt;i+=2)
    {
        int u=e[i].u,v=e[i].v,w=e[i].w;
        if(dep[u]<dep[v])
            wt[id[v]]=w;
        else
            wt[id[u]]=w;
    }
    build(1,1,N);
    scanf("%d",&Q);
    while(Q--)
    {
        int u,v;
        scanf("%d %d",&u,&v);
        if(u>v)
            swap(u,v);
        printf("%d\n",min_sum-tree_query(u,v)+cost[make_pair(u,v)]);
    }
	return 0;
}
发布了214 篇原创文章 · 获赞 40 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Ratina/article/details/101372426