noi.ac 41 最短路 题解

放肆的年华 提交于 2020-02-28 05:08:24

博客观赏效果更佳

题意简述

给你一个 nn 个点的边带权的树,还有 mm 个新增的修建计划,以及 QQ 个询问。每一个询问的格式是:给定 s,t,l,rs,t,l,r ,问你,如果动用 [l,r][l,r] 之间的修建计划,从 sstt 的路径中,边权异或和最小是多少?

询问之间是独立的,在某一个询问里加入的修建计划,询问完就会拆掉。并且修建计划保证不是树上原来就有的边。

n,m,q3×105n,m,q\le 3\times 10^5,所有的边权(树上和修建计划) 109\le 10^9。对于每个询问,1s,tn1\le s,t\le n,并且1lrm1\le l\le r\le m

思路

bzoj 2115 的结论得,一张图上从 sstt 的路径的异或和,可以由另外一条路径的异或和,异或上几个环的异或和得到。

然后我们珂以先取初始值为 sstt 树上路径的异或和,然后在把所有环的异或和放到线性基里,求最小异或和。

本题还限制了只能动用 [l,r][l,r] 之间的修建计划。那就按 rr 排个序,对于每个位置 ii ,记录所有 r=ir=i 的询问。然后在插入线性基的时候,顺便维护上修建计划的编号。对于每个询问,我们只考虑 [1,r][1,r] 的修建计划,不用考虑动用 $>r $的修建计划的问题,只要满足修建计划的编号 l\ge l 即珂。线性基求最小异或和的时候,顺便维护下即珂。

代码

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
    #define N 355555
    #define F(i,l,r) for(int i=l;i<=r;++i)
    #define D(i,r,l) for(int i=r;i>=l;--i)
    #define Fs(i,l,r,c) for(int i=l;i<=r;c)
    #define Ds(i,r,l,c) for(int i=r;i>=l;c)
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)
    #define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
    #define p_b push_back
    #define sz(a) ((int)a.size())
    #define iter(a,p) (a.begin()+p)
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void Rd(int cnt,...)
    {
        va_list args;
        va_start(args,cnt);
        F(i,1,cnt) 
        {
            int* x=va_arg(args,int*);R1(*x);
        }
        va_end(args);
    }
    class Graph
    {
        public:
            int head[N];
            int EdgeCount;
            struct Edge
            {
                int To,Label,Next;
            }Ed[N<<1];
            void clear(int _V=N,int _E=N<<1) 
            {
                memset(Ed,-1,sizeof(Edge)*(_E));
                memset(head,-1,sizeof(int)*(_V));
                EdgeCount=-1;
            }
            void AddEdge(int u,int v,int w=1)
            {
                Ed[++EdgeCount]=(Edge){v,w,head[u]};
                head[u]=EdgeCount;
            }
            void Add2(int u,int v,int w=1) {AddEdge(u,v,w);AddEdge(v,u,w);}
            int Start(int u) {return head[u];}
            int To(int u){return Ed[u].To;}
            int Label(int u){return Ed[u].Label;}
            int Next(int u){return Ed[u].Next;}
    }G;
    
    int n,m,q;
    void Input()
    {
        Rd(3,&n,&m,&q);
        G.clear();
        F(i,1,n-1) 
        {
            int u,v,w;Rd(3,&u,&v,&w);
            G.Add2(u,v,w);
        }
    }
    int d[N];
    void DFS(int u,int f) //先预处理出每个点到根的异或和:d[i]
    {
        Tra(i,u)
        {int v=__v;
            if (v!=f)
            {
                d[v]=d[u]^G.Label(i);
                DFS(v,u);
            }
        }
    }
    int w[N];
    int l[N],r[N];
    int p[32],id[32]; //p是线性基,id是线性基顺便维护的修建计划编号
    vector<int> pos[N];
    int ans[N];
    void Soviet()
    {
        DFS(1,1);
        F(i,1,m) 
        {
            int u,v,len;Rd(3,&u,&v,&len);
            w[i]=d[u]^d[v]^len; //第i个修建计划从u到v,那就会产生一个环,这个环的异或和为:u到v树上路径的异或和,再以后上修建计划的边权
        }
        F(i,1,q) 
        {
            int s,t;Rd(2,&s,&t);
            ans[i]=d[s]^d[t]; //初始答案就是s到t的树上路径异或
            Rd(2,&l[i],&r[i]); 
            pos[r[i]].p_b(i); //离线,按r排序,把r相同的询问一块考虑
        }
        F(t,1,m)
        {
            int x=w[t],r=t;
            D(i,30,0) if ((x>>i)&1) 
            {
                if (!p[i]){p[i]=x;id[i]=r;break;}
                if (id[i]<r) swap(p[i],x),swap(id[i],r);
                x^=p[i];
            } //插入线性基的时候顺便维护编号
            F(k,0,sz(pos[t])-1)
            {int v=pos[t][k];
                D(i,30,0)
                {
                    if (id[i]>=l[v]) ans[v]=min(ans[v],ans[v]^p[i]);
                 //在考虑编号>=l的情况下,求最小异或和
                }
            }
        }
        F(i,1,q) printf("%d\n",ans[i]);
    }

    #define Flan void
    Flan IsMyWife()
    {
        Input();
        Soviet();
    }
}
int main()
{
    Flandre_Scarlet::IsMyWife();
    getchar();getchar();
    return 0;
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!