hdu 5039 Hilarity(dfs序 + 线段树)

题意:给出一颗树,边上有权值(0~1),有两种操作,一种是修改某个边的权值,另一种是查询当前的树有多少个不同的路径,使得路径上的权值异或和为1。

思路:刚开始往点分治想了,想了半天感觉很不好做。其实发现这题是路径上的异或,那么就有更简单的做法了,因为不用真的求出所有的路径,确定一个根,那么两个点路径的权值异或和可以用它们到跟的路径的权值异或和表示,这样,只需要维护每个边到跟的路径的权值的异或和就行了。dfs一下,把树形结构转为线性结构,用线段树维护,然后就可以做了……


代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-6
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn = 30000 + 10;
struct Edge
{
    int v,color,next;
    Edge(int v = 0,int color = 0,int next = 0):v(v),color(color),next(next){}
}edges[maxn<<1];
int head[maxn],Lx[maxn],Rx[maxn],N,nEdge;
int a[maxn];
map<string,int>mp;
void AddEdges(int u,int v,int color)
{
    edges[++nEdge] = Edge(v,color,head[u]);
    head[u] = nEdge;
    edges[++nEdge] = Edge(u,color,head[v]);
    head[v] = nEdge;
}
void dfs(int u,int fa,int c)
{
    for(int k = head[u];k != -1;k = edges[k].next)
    {
        int v = edges[k].v;
        if(v == fa) continue;
        Lx[k/2] = ++N;
        a[N] = c^edges[k].color;
        dfs(v,u,c^edges[k].color);
        Rx[k/2] = N;
    }
}
int sum[maxn<<2],flip[maxn<<2];
inline void PushUp(int rt)
{
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void PushDown(int l,int r,int rt)
{
    if(flip[rt])
    {
        flip[rt<<1] ^= 1;
        flip[rt<<1|1] ^= 1;
        int m = (l + r)>>1;
        sum[rt<<1] = (m - l + 1) - sum[rt<<1];
        sum[rt<<1|1] = (r - m) - sum[rt<<1|1];
        flip[rt] = 0;
    }
}
void build(int l,int r,int rt)
{
    flip[rt] = 0;
    if(l == r)
    {
        sum[rt] = a[l];
        return ;
    }
    int m = (l + r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    PushUp(rt);
}
void Update(int L,int R,int l,int r,int rt)
{
    if(l >= L && r <= R)
    {
        flip[rt] ^= 1;
        sum[rt] = r - l + 1 - sum[rt];
        return ;
    }
    PushDown(l,r,rt);
    int m = (l + r)>>1;
    if(m >= L) Update(L,R,l,m,rt<<1);
    if(m < R) Update(L,R,m+1,r,rt<<1|1);
    PushUp(rt);
}
void Init()
{
    memset(head,0xff,sizeof(head));
    nEdge = -1; N = 0;
    mp.clear();
}
int main()
{
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    char str[20];
    int t,tcase = 0;
    scanf("%d",&t);
    while(t--)
    {
        Init();
        int n,m;
        scanf("%d",&n);
        for(int i = 1;i <= n;++i)
        {
            scanf("%s",str);
            mp[(string)str] = i;
        }
        int u,v,c;
        for(int i = 1;i < n;++i)
        {
            scanf("%s",str);
            u = mp[(string)str];
            scanf("%s",str);
            v = mp[(string)str];
            scanf("%d",&c);
            AddEdges(u,v,c);
        }
        dfs(1,-1,0);
        build(1,N,1);
        scanf("%d",&m);
        printf("Case #%d:\n",++tcase);
        while(m--)
        {
            scanf("%s",str);
            if(str[0] == 'Q')
            {
                ll x = sum[1];
                printf("%I64d\n",x*(n - x)*2LL);
            }
            else
            {
                scanf("%d",&u);
                Update(Lx[u-1],Rx[u-1],1,N,1);
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值