1. 程式人生 > >【ICPC2017 Urumqi 新疆區域賽 烏魯木齊】UPC-5220 A Possible Tree(樹上路徑異或和判斷)

【ICPC2017 Urumqi 新疆區域賽 烏魯木齊】UPC-5220 A Possible Tree(樹上路徑異或和判斷)

題目描述
Alice knows that Bob has a secret tree (in terms of graph theory) with n nodes with n − 1 weighted edges with integer values in [0, 260 −1]. She knows its structure but does not know the specific information about edge weights.
Thanks to the awakening of Bob’s conscience, Alice gets m conclusions related to his tree. Each conclusion provides three integers u, v and val saying that the exclusive OR (XOR) sum of edge weights in the unique shortest path between u and v is equal to val.
Some conclusions provided might be wrong and Alice wants to find the maximum number W such that the first W given conclusions are compatible. That is say that at least one allocation of edge weights satisfies the first W conclusions all together but no way satisfies all the first W + 1 conclusions (or there are only W conclusions provided in total).
Help Alice find the exact value of W.

輸入
The input has several test cases and the first line contains an integer t (1 ≤ t ≤ 30) which is the number of test cases.
For each case, the first line contains two integers n (1 ≤ n ≤ 100000) and c (1 ≤ c ≤ 100000) which are the number of nodes in the tree and the number of conclusions provided. Each of the following n−1 lines contains two integers u and v (1 ≤ u, v ≤ n) indicating an edge in the tree between the u-th node and the v-th node. Each of the following c lines provides a conclusion with three integers u, v and val where 1 ≤ u, v ≤ n and val ∈ [0, 260 − 1].

輸出
For each test case, output the integer W in a single line.

樣例輸入
2
7 5
1 2
2 3
3 4
4 5
5 6
6 7
1 3 1
3 5 0
5 7 1
1 7 1
2 3 2
7 5
1 2
1 3
1 4
3 5
3 6
3 7
2 6 6
4 7 7
6 7 3
5 4 5
2 5 6

樣例輸出
3
4

題意:給出一棵有N個節點的樹,沒有給邊權,再出q條路徑和,每個路徑和w都是不一定爭取的,現在讓你判斷這樣一棵樹最多能有多少路徑和符合條件。這裡的最多指的是,q條路徑順序下來從1~q,只要遇到一個不符合條件的,那麼這個ans即第一個不符合條件的路徑的下標-1。
如有5條路徑和,1,2,3都符合條件,4不符合條件,那麼即使5符合條件,ans也是3

因為沒有給邊權,那麼一開始給的n-1個樹的構成其實就是無用的。輸入直接忽視掉就好。真正有用的,用來判斷是否成立的是下面給的q條路徑。我們假設所有路徑和都是正確的,有路徑和說明兩結點聯通,並且在樹上只有唯一一條路徑,因此可以直接建邊。邊權為路徑和,那麼有個問題是當1~3、3~5給出路徑和後,又給出了1~5也有路徑和,這樣建圖就會出現環,那麼很明顯,如果前兩條路徑和先輸入的話,那麼直接預設為正確的,因為如果前兩條都不能滿足,後面的就直接無用了。建圖後,遇到了1~5的路徑和。如果建邊將出現環,因此不建邊,該條路徑和的作用是在用前面儘可能多的路徑建樹之後,隨意取一點做根節點,計算路徑和,並與未建邊的,可能成環的路徑和進行判斷,符合條件那麼就繼續遍歷,ans++,否則直接退出輸出ans。

建圖過程中用並查集判斷是否成環,剩下的計算每個節點的路徑和就深搜走一遍樹即可。

#include<bits/stdc++.h>
#define LL long long
#define M(a,b) memset(a,b,sizeof a)
#define pb(x) push_back(x)
using namespace std;
const int maxn=1e5+7;
int n,t,q;
struct edge
{
    int to;
    LL val;
    edge() {}
    edge(int a,LL b)
    {
        to=a;
        val=b;
    }
};
vector<edge>mp[maxn];
int z[maxn];
LL sum[maxn];
bool vis[maxn];
int finds(int x)
{
    return z[x]==x?x:z[x]=finds(z[x]);
}
void dfs(int rt,int fa)
{
    if(vis[rt])return;
    vis[rt]=true;
    for(int i=0; i<mp[rt].size(); i++)
    {
        edge tmp=mp[rt][i];
        if(tmp.to==fa)continue;
        sum[tmp.to]=sum[rt]^tmp.val;///異或路徑和
        dfs(tmp.to,rt);
    }
    return;
}
int from[maxn],to[maxn];
LL val[maxn];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&q);
        for(int i=0; i<=n; i++)mp[i].clear(),z[i]=i,vis[i]=false,sum[i]=0;///初始化
        for(int i=0; i<n-1; i++) scanf("%*d%*d");
        for(int i=1; i<=q; i++)
        {///真正有效的是給了路徑異或和的資訊,那麼,就把所有可能的路徑異或和都認為是正確的
            scanf("%d%d%lld",&from[i],&to[i],&val[i]);///有q條不確定正確與否的異或路徑,但其實上面給出的樹圖沒有給出邊權所以沒有什麼用
            int fx=finds(from[i]),fy=finds(to[i]);///並查集判斷是否有環,有環不建邊
            if(fx!=fy)///給出的q條可能路徑,首先因為肯定聯通並且只有唯一路徑,那麼這條邊實際上是有效的,只是不確定異或和正確與否
            {
                z[fy]=fx;
                mp[from[i]].pb(edge(to[i],val[i]));
                mp[to[i]].pb(edge(from[i],val[i]));
            }///如果需要最大化正確的路徑異或和的條數,那就將前幾條預設是對的,在此基礎上建圖去判斷後面其他條,這樣順序判斷下去一定是最大的條數
        }
        for(int i=1; i<=q; i++)///因為q條可能給出來的並不是一顆完整的樹,可能是多棵,所以要帶標記的深搜每顆樹算到異或路徑和
            if(!vis[from[i]])
                dfs(from[i],0);
        int ans=0;
        for(int i=1; i<=q; i++)///最後得到所有建立在前幾條可能異或和的樹上判斷往後最多有多少條可行即可
            if((sum[from[i]]^sum[to[i]])!=val[i])break;
            else ans++;
        printf("%d\n",ans);
    }
}