【ICPC2017 Urumqi 新疆区域赛 乌鲁木齐】UPC-5220 A Possible Tree(树上路径异或和判断)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kuronekonano/article/details/81975430

题目描述
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);
    }
}

猜你喜欢

转载自blog.csdn.net/kuronekonano/article/details/81975430
今日推荐