2019.11.13 基礎最短路練習題

題目背景

\(YSGH\)牛逼ios

題目描述

給定\(n\)個點\(m\)條邊的無向簡單聯通圖\(G\),邊有邊權。保證沒有重邊和自環。git

定義一條簡單路徑的權值爲路徑上全部邊邊權的異或和。優化

保證\(G\)中不存在簡單環使得邊權異或和不爲\(0\)spa

\(Q\)次詢問\(x\)\(y\)的最短簡單路徑。code

輸入格式

第一行三個正整數\(n,m,Q\)get

接下來\(m\)行,一行三個非負整數\(x,y,v(1 \leq x,y \leq n)\),表示一條鏈接\(x,y\),權值爲\(v\)的無向邊。保證沒有重邊和自環。string

接下來\(Q\)行,一行兩個正整數\(x,y(1 \leq x,y \leq n)\),表示一次詢問。it

輸出格式

\(Q\)行,一行一個整數表示答案。io

輸入輸出樣例

輸入 #1table

3 2 1
1 2 2
2 3 3
1 3

輸出 #1

1

說明/提示

數據點編號 \(n,Q\leq\) 特殊性質
\(1,2\) \(10\)
\(3,4\) \(20\)
\(5,6\) \(10^5\) \(m = n-1\)
\(7,8\) \(10^5\) \(v \leq 1\)
\(9,10\) \(10^5\)

對於全部數據,知足\(m \leq 2\times n,v < 2^{30}\)

剛看到這個題實在沒什麼想法,由於對帶有異或和類的題目作的太少。

後來聽人講纔想起來,這個題的突破口在於任意一個環的異或和都是\(0\)。以及咱們還須要一個推論,從一條路徑增廣到另外一條路徑的前提是這兩條路徑必須可以構成一個環,也就是這兩條路徑的異或和的異或和是\(0\)。因此,這兩條路徑異或和相等。

對上述結論推廣,因此,任意兩點之間的全部合法路徑異或和相等。

也就是說,咱們只須要找到兩點間任意一條合法路徑便可。

根據此優化圖的模型,咱們發現只要兩點之間有路便可,即咱們能夠將其優化成一棵樹。因此咱們能夠用\(O(n)\)的時間預處理每一個點到根路徑的異或和,對於每組詢問的\(x,y\),只須要輸出\(dp[x]\veebar dp[y]\),中間那個符號是邏輯裏的異或。這是由於從\(lca(x,y)\)到根的路徑走了兩次,抵消掉了,再次利用了異或的性質。

上代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#define int long long
#define rep(i,a,n) for(register int i=a;i<=n;i++)
#define dep(i,n,a) for(register int i=n;i>=a;i--)
using namespace std;
int n,m,head[100050],dp[100050],num,num1,head1[100050],fa[100050],q;
struct edge
{
    int u,v,c,nxt;
}e[1000500],e1[100050];
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
void write(int x)
{
    if(x<0)putchar('-'),x=-x;
    if(x==0)return;
    write(x/10);
    putchar(x%10+'0');
}
void add(int u,int v,int c)
{
    e[++num].u=u,e[num].v=v,e[num].c=c;
    e[num].nxt=head[u];head[u]=num;
}
void add1(int u,int v,int c)
{
    e1[++num1].u=u,e1[num1].v=v,e1[num1].c=c;
    e1[num1].nxt=head1[u];head1[u]=num1;
}
int f(int o)
{
    if(fa[o]==o)return o;
    return fa[o]=f(fa[o]);
}
void u(int a,int b)
{
    int c=f(a),d=f(b);
    fa[max(c,d)]=fa[min(c,d)];
}
void DP(int x,int fa)
{
    for(register int st=head1[x];~st;st=e1[st].nxt)
    {
        int y=e1[st].v;
        if(y==fa)continue;
        dp[y]=dp[x]^e1[st].c;
        DP(y,x);
    }
    return;
}
signed main()
{
    memset(head,-1,sizeof head);
    memset(head1,-1,sizeof head1);
    n=read(),m=read(),q=read();
    int a,b,c;
    rep(i,1,m)
    {
        a=read(),b=read(),c=read();
        add(a,b,c);
        add(b,a,c);
    }
    int cnt=0;
    rep(i,1,n)fa[i]=i;
    rep(i,1,num)
    {
        int x=e[i].u,y=e[i].v;
        if(f(x)!=f(y))
        {
            ++cnt;
            u(x,y);
            add1(x,y,e[i].c);
            add1(y,x,e[i].c);
        }
        if(cnt==n-1)break;
    }//連成一棵生成樹
    DP(1,-1);
    rep(i,1,q)
    {
        a=read(),b=read();
        int ans=dp[a]^dp[b];
        if(ans)write(ans);
        else putchar('0');
        putchar('\n');
    }
    return 0;
}
相關文章
相關標籤/搜索