hdu 4897 树链剖分(重轻链)

Little Devil I

Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 894    Accepted Submission(s): 296

Problem Description

There is an old country and the king fell in love with a devil. The devil always asks the king to do some crazy things. Although the king used to be wise and beloved by his people. Now he is just like a boy in love and can’t refuse any request from the devil. Also, this devil is looking like a very cute Loli.

The devil likes to make thing in chaos. This kingdom’s road system is like simply a tree(connected graph without cycle). A road has a color of black or white. The devil often wants to make some change of this system.

In details, we call a path on the tree from a to b consists of vertices lie on the shortest simple path between a and b. And we say an edge is on the path if both its two endpoints is in the path, and an edge is adjacent to the path if exactly one endpoint of it is in the path.

Sometimes the devil will ask you to reverse every edge’s color on a path or adjacent to a path.

The king’s daughter, WJMZBMR, is also a cute loli, she is surprised by her father’s lolicon-like behavior. As she is concerned about the road-system’s status, sometimes she will ask you to tell there is how many black edge on a path.

Initially, every edges is white.

Input

The first line contains an integer T, denoting the number of the test cases.
For each test case, the first line contains an integer n, which is the size of the tree. The vertices be indexed from 1.
On the next n-1 lines, each line contains two integers a,b, denoting there is an edge between a and b. 
The next line contains an integer Q, denoting the number of the operations.
On the next Q lines, each line contains three integers t,a,b. t=1 means we reverse every edge’s color on path a to b. t=2 means we reverse every edge’s color adjacent to path a to b. t=3 means we query about the number of black edge on path a to b.

T<=5.
n,Q<=10^5.
Please use scanf,printf instead of cin,cout,because of huge input.

Output

For each t=3 operation, output the answer in one line.

Sample Input

1
10
2 1
3 1
4 1
5 1
6 5
7 4
8 3
9 5
10 6

10
2 1 6
1 3 8
3 8 10
2 3 4
2 10 8
2 4 10
1 7 6
2 7 3
2 1 4
2 10 10

Sample Output

3

/*
hdu 4897 树链剖分(重轻链)

problem:
给你一棵树,初始每条边为白色,然后是三种操作
1.将u->v链上面的所有边的颜色翻转 (例:white -> black)
  这个在线段树上很好处理,用个翻转标记,然后记录数量即可
2.将u->v链上面所有邻接的边翻转(边上只有一个点在链上面)
3.询问u->v上面有多少黑色的边

solve:
对于1,3操作树链剖分很好解决。但是在操作2上面就GG了.所以去参考了很多博客 - -,很久才明白大致思路

就操作2而言,主要可以看成在一条重链上面的 和 跨越了很多重轻链的那种.
主要是轻链两端连接的是重链,所以在操作2的时候可以考虑直接在每个点上面打标记(除了有的叶子节点,重链基本上
覆盖了所有的点).
所以轻链的颜色就是:  左端点rev2^右端点rev2^边的颜色(边的颜色线段树很好维护的)
如果重链分成很多条边来用也可以实现,但是无疑到达lca的效率为很低,所有需要考虑其他方法

然后就是维护重链上面的颜色,如果u,v在一条重链的中间部分,打标记可以维护对轻链的影响。所以只需要考虑对两端重链
的影响,于是把与两端相邻的边用操作1翻转就好了.在操作2下一条重链最多只需要更新左右两个端点,但是却有很多条
轻链。 所以重链可以直接更新,轻链则需要标记来维护了。

因为没有判断,有时线段树会出现l>r导致RE了很久- -
hhh-2016-08-18 21:18:55
*/
#pragma comment(linker,"/STACK:124000000,124000000")
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#define lson  i<<1
#define rson  i<<1|1
#define ll long long
#define clr(a,b) memset(a,b,sizeof(a))
#define key_val ch[ch[root][1]][0]
using namespace std;
const int maxn = 200100;
const int inf = 0x3f3f3f3f;
int head[maxn],tot,pos,son[maxn];
int top[maxn],fp[maxn],fa[maxn],dep[maxn],num[maxn],p[maxn];
int n;
struct Edge
{
    int to,next;
} edge[maxn<<2];

void ini()
{
    tot = 0,pos = 1;
    clr(head,-1),clr(son,-1);
}

void add_edge(int u,int v)
{
    edge[tot].to = v,edge[tot].next = head[u],head[u] = tot++;
}

void dfs1(int u,int pre,int d)
{
//    cout << u << " " <<pre <<" " <<d <<endl;
    dep[u] = d;
    fa[u] = pre,num[u] = 1;
    for(int i = head[u]; ~i; i = edge[i].next)
    {
        int v = edge[i].to;
        if(v != pre)
        {
            dfs1(v,u,d+1);
            num[u] += num[v];
            if(son[u] == -1 || num[v] > num[son[u]])
                son[u] = v;
        }
    }
}

void getpos(int u,int sp)
{
    top[u] = sp;
    p[u] = pos++;
    fp[p[u]] = u;
    if(son[u] == -1)return ;
    getpos(son[u],sp);
    for(int i = head[u]; ~i ; i = edge[i].next)
    {
        int v = edge[i].to;
        if(v != son[u] && v != fa[u])
            getpos(v,v);
    }
}

struct node
{
    int l,r,mid;
    int rev1,rev2;
    int num;
} tree[maxn << 2];

void push_up(int i)
{
    tree[i].num = tree[lson].num + tree[rson].num;
}

void build(int i,int l,int r)
{
    tree[i].l = l,tree[i].r = r;
    tree[i].mid=(l+r) >>1;
    tree[i].rev1 = tree[i].rev2 = 0;
    tree[i].num = 0;
    if(l == r)
    {
//        cout << fp[l] <<" " <<val[fp[l]]<<endl;
        return;
    }
    build(lson,l,tree[i].mid);
    build(rson,tree[i].mid+1,r);
}

void push_down(int i)
{
    if(tree[i].rev1)
    {
        tree[i].rev1 = 0;
        tree[lson].rev1 ^= 1,tree[lson].num = tree[lson].r-tree[lson].l+1-tree[lson].num;
        tree[rson].rev1 ^= 1,tree[rson].num = tree[rson].r-tree[rson].l+1-tree[rson].num;;
    }
    if(tree[i].rev2)
    {
        tree[i].rev2 = 0;
        tree[lson].rev2 ^= 1;
        tree[rson].rev2 ^= 1;
    }
}

void update_area(int i,int l,int r,int flag)
{
//    cout <<"l:"<< l <<" r:"<<r <<" min:"<< tree[i].Min<<endl;
    if(l > r)
        return ;
    if(tree[i].l >= l && tree[i].r <= r)
    {
        if(flag == 1)
        {
            tree[i].num = tree[i].r-tree[i].l+1-tree[i].num;
            tree[i].rev1 ^= 1;
        }
        else
            tree[i].rev2 ^= 1;
        return ;
    }
    push_down(i);
    int mid = tree[i].mid;
    if(r <= mid)
        update_area(lson,l,r,flag);
    else if(l > mid)
        update_area(rson,l,r,flag);
    else
    {
        update_area(lson,l,mid,flag);
        update_area(rson,mid+1,r,flag);
    }
    push_up(i);
}

int query(int i,int l,int r,int flag)
{
    if(l > r)
        return 0;
    if(tree[i].l >= l && tree[i].r <= r)
    {
        if(flag == 1)
            return tree[i].num;
        else
            return tree[i].rev2;
    }
    push_down(i);
    int mid = tree[i].mid;
    if(r <= mid)
        return query(lson,l,r,flag);
    else if(l > mid)
        return query(rson,l,r,flag);
    else
        return query(lson,l,mid,flag)+query(rson,mid+1,r,flag);
    push_up(i);
}

void update_rev1(int u,int v)
{
    int f1 = top[u],f2 = top[v];
    while(f1 != f2)
    {
        if(dep[f1] < dep[f2])
        {
            swap(f1,f2),swap(u,v);
        }
        update_area(1,p[f1],p[u],1);
        u = fa[f1],f1 = top[u];
    }
    if(dep[u] > dep[v]) swap(u,v);
    update_area(1,p[son[u]],p[v],1);
}

void update_rev2(int u,int v)
{
    int f1 = top[u],f2 = top[v];
//    cout << u << " " <<v<<endl;
    while(f1 != f2)
    {
        if(dep[f1] < dep[f2])
        {
            swap(f1,f2),swap(u,v);
        }
        update_area(1,p[f1],p[u],2);
        int par = fa[f1];
        if(son[par] == f1) update_area(1,p[f1],p[f1],1);
        if(son[u] != -1)  update_area(1,p[son[u]],p[son[u]],1);
        u = fa[f1],f1 = top[u];
    }
    if(dep[u] > dep[v]) swap(u,v);

    update_area(1,p[u],p[v],2);
    int par = fa[u];
//    cout <<par <<" "<< son[v] <<endl;
    if(son[par] == u && par > 0) update_area(1,p[u],p[u],1);
    if(son[v] != -1)  update_area(1,p[son[v]],p[son[v]],1);
}

int Find(int u,int v)
{
//    cout <<"*********************************************************"<<endl;
    int f1 = top[u],f2 = top[v];
    int ans = 0;
//    cout << u << " " <<v<<endl;

    while(f1 != f2)
    {
        if(dep[f1] < dep[f2])
        {
            swap(f1,f2),swap(u,v);
        }
        ans += query(1,p[f1]+1,p[u],1);
//        cout <<ans <<" " <<f1 <<" " <<u <<endl;
        ans += query(1,p[fa[top[u]]],p[fa[top[u]]],2)^query(1,p[top[u]],p[top[u]],2)
               ^query(1,p[top[u]],p[top[u]],1);
//        cout <<ans<<" "<<fa[f1]<<" "<<u <<endl;
        u = fa[f1],f1 = top[u];
    }
    if(dep[u] > dep[v]) swap(u,v);
//    cout << query(1,p[u]+1,p[v],1) <<endl;
    return ans+query(1,p[u]+1,p[v],1);
}

int main()
{
//    freopen("in.txt","r",stdin);
    int T,cas = 1,op;
    int a,b;
    int m,u,v;
    scanf("%d",&T);
    while(T--)
    {
        ini();
        scanf("%d",&n);
        for(int i =1; i <n; i++)
        {
            scanf("%d%d",&u,&v);
            add_edge(u,v);
            add_edge(v,u);
        }
        dfs1(1,0,0);
        getpos(1,1);
        build(1,1,pos-1);
        scanf("%d",&m);
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d%d",&op,&a,&b);
//            cout << op <<" " <<a <<" " <<b<<endl;
            if(op == 1)
            {
                if(a == b)
                    continue;
                update_rev1(a,b);
            }
            else if(op == 2)
            {
                update_rev2(a,b);
            }
            else if(op == 3)
            {
                printf("%d\n",Find(a,b));
            }
        }
    }
    return 0;
}

  

时间: 2024-12-26 13:03:49

hdu 4897 树链剖分(重轻链)的相关文章

hdu 5893 (树链剖分+合并)

List wants to travel Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 429    Accepted Submission(s): 92 Problem Description A boy named List who is perfect in English. Now he wants to travel an

hdu 5274 树链剖分

Dylans loves tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1484    Accepted Submission(s): 347 Problem Description Dylans is given a tree with N nodes. All nodes have a value A[i].Nodes

hdu 5052 树链剖分

Yaoge’s maximum profit Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 982    Accepted Submission(s): 274 Problem Description Yaoge likes to eat chicken chops late at night. Yaoge has eaten too

hdu 5242 树链剖分找权值最大的前k条链

http://acm.hdu.edu.cn/showproblem.php?pid=5242 Problem Description It is well known that Keima Katsuragi is The Capturing God because of his exceptional skills and experience in ''capturing'' virtual girls in gal games. He is able to play k games sim

HDU 3966 (树链剖分+线段树)

Problem Aragorn's Story (HDU 3966) 题目大意 给定一颗树,有点权. 要求支持两种操作,将一条路径上的所有点权值增加或减少ai,询问某点的权值. 解题分析 树链剖分模板题. 实质上树链剖分进行了点对点的一次映射,保证了重链上的点在线段树上的位置是连续的. 树链剖分的两个性质(转): 性质1:如果(v,u)为轻边,则siz[u] * 2 < siz[v]: 性质2:从根到某一点的路径上轻链.重链的个数都不大于logn. 保证了一个区间的时间复杂度是log2(n).

HDU 5029 树链剖分

Relief grain Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 100000/100000 K (Java/Others) Total Submission(s): 861    Accepted Submission(s): 219 Problem Description The soil is cracking up because of the drought and the rabbit kingdom is f

HDU 5274(树链剖分)

树链剖分第一题QAQ,纪念下 #pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long ll; const ll mod = 1e9 + 7; const int maxn = 1e5 + 10; #define

hdu 3804树链剖分+离线操作

/* 树链刨分+离线操作 题意:给你一棵树,和询问x,y 从节点x--节点1的小于等于y的最大值. 解:先建一个空树,将树的边权值从小到大排序,将询问y按从小到大排序 对于每次询问y将小于等于y的边权值的边加入,在进行询问将结果储存最后输出即可 易错点:要考虑到节点1到节点1的情况需特判. */ #pragma comment(linker, "/STACK:102400000,102400000") #include<stdio.h> #include<string

HDU 5405 (树链剖分+线段树)

Problem Sometimes Naive 题目大意 给你一棵n个节点的树,有点权. 要求支持两种操作: 操作1:更改某个节点的权值. 操作2:给定u,v, 求 Σw[i][j]   i , j 为任意两点且i到j的路径与u到v的路径相交. 解题分析 容易发现对于一个询问,答案为总点权和的平方 减去 去掉u--v这条链后各个子树的点权和的平方的和. 开两棵线段树,tag1记录点权和,tag2记录某点的所有轻链子树的点权和的平方的和. 每次沿着重链往上走时,直接加上这条重链的所有点的tag2和