hdu 5416 CRB and Tree 离线处理树xor前缀

题意:给出一棵树,和树边的权值,q个询问,每次给出s求有多少条路径上边权值异或值为s。

由于可得f(u,v) = f(1,u)\ f(1, u) f(1,u) ^f(1,v) f(1, v)f(1,v), 那么就离线处理出以1为树根的树xor前缀,并hash记录该异或值数量,对于每个询问s,遍历1~n,累计num【xor【i】】即可,注意s = 0时,所有的值1~n都满足(x^x = 0),要多加一个n,最后结果/2

#include <bits/stdc++.h>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <cmath>
#include <time.h>
#include <vector>
#include <cstdio>
#include <string>
#include <iomanip>
///cout << fixed << setprecision(13) << (double) x << endl;
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define ls rt << 1
#define rs rt << 1 | 1
#define pi acos(-1.0)
#define eps 1e-8
#define Mp(a, b) make_pair(a, b)
#define asd puts("asdasdasdasdasdf");
typedef long long ll;
typedef pair <int, int> pl;
//typedef __int64 LL;
const int inf = 0x3f3f3f3f;
const int N = 100010;

ll xr[N];
int head[N];
int n, cnt, q;
ll num[N*20];

struct node{
    int v, w, nxt;
}e[N<<1];

void init()
{
    cnt = 0;
    memset( num, 0, sizeof( num ) );
    memset( head, -1, sizeof( head ) );
    memset( xr, 0, sizeof( xr ) );
}

void add( int u, int v, int w )
{
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].nxt = head[u];
    head[u] = cnt++;

    e[cnt].v = u;
    e[cnt].w = w;
    e[cnt].nxt = head[v];
    head[v] = cnt++;
}

void dfs( int u, int fa )
{
    for( int i = head[u]; ~i; i = e[i].nxt ) {
        int v = e[i].v, w = e[i].w;
        if( v == fa )
            continue;
        xr[v] = xr[u] ^ w;
        dfs( v, u );
    }
}

int main()
{
    int tot;
    scanf("%d", &tot);
    while( tot-- ) {
        scanf("%d", &n);
        init();
        for( int i = 1, u, v, w; i < n; ++i ) {
            scanf("%d%d%d", &u, &v, &w);
            add( u, v, w );
        }
        dfs( 1, -1 );
        for( int i = 1; i <= n; ++i )
            num[ xr[i] ]++;
        scanf("%d", &q);
        while( q-- ) {
            ll ans = 0, zero = 0;
            ll tmp;
            scanf("%lld", &tmp);
            for( int i = 1; i <= n; ++i ) {
                ll x = tmp ^ xr[i];
                ans += num[x];
                if( tmp == 0 )
                    zero = 1;
            }
            ans += zero * n;
            printf("%lld\n", ans/2);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值