noi.ac 41 最短路 題解

博客觀賞效果更佳

題意簡述

給你一個 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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章