hdu 4897 Little Devil I 树链剖分

来源:互联网 发布:淘宝国际版开店 编辑:程序博客网 时间:2024/05/02 15:55

Little Devil I

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


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
1102 13 14 15 16 57 48 39 510 6102 1 61 3 83 8 102 3 42 10 82 4 101 7 62 7 32 1 42 10 10
 

Sample Output
3
Hint
reverse color means change from white to black or vice virsa.
 

Author
WJMZBMR
 
在树上有两种操作,一种是把该链上的边颜色翻转,第二种是把与该链相邻的链进行翻转。然后求一条链上黑色的数目

只会裸的树链剖分果然还是不行啊。。

参考了

http://blog.csdn.net/u010089558/article/details/38334723

的思路和代码,重新打了一遍,感觉理解更加深刻了。


一整条重链上面必然有一条轻链。

重链的最底端一定是叶子节点。

要知道轻链是否翻转,其实只需要标记该轻链的下边的那个节点所在的重链有没有进行第二中操作过。

每次重链都能够一整条直接处理,轻链则是一段一段处理。



树链剖分的优势就是可以很快的处理一段长的重链。
所以做这类题目要尽量把操作放在重链。
对于第一种操作,裸的树链剖分可以解决。
第二种操作,可以再新建一颗线段树,记录哪些重边的相邻的边是否已经改变。
用一个col标志记录一个重链顶端上面的轻链是否发生过翻转。
所以一条轻链只和他上面的那个点以及那个col标志有关。
而如果改变的不是重链顶端,则需要改对应的上面的重链。

还有一点要注意,sumTree是边的,markTree是点的。

/***********************************************\ |Author: Messyidea |Created Time: 2014-8-2 13:48:06 |File Name: 1001.cpp |Description: \***********************************************/#include <iostream>#include <cstdio>#include <cmath>#include <cstdlib>#include <string>#include <cstring>#include <algorithm>#include <vector>#include <list>#include <map>#include <set>#include <deque>#include <queue>#include <stack>#define L(rt) (rt<<1)#define R(rt) (rt<<1|1)#define lc (tree[rt].ls)#define rc (tree[rt].rs)#define mset(l,n) memset(l,n,sizeof(l))#define rep(i,n) for(int i=0;i<n;++i)#define maxx(a) memset(a, 0x3f, sizeof(a))#define zero(a) memset(a, 0, sizeof(a))#define srep(i,n) for(int i = 1;i <= n;i ++)#define MP make_pairconst int inf=0x3f3f3f3f ;const double eps=1e-8 ;const double pi=acos (-1.0);typedef long long ll;using namespace std;#define maxn 100005vector <int> ma[maxn];int que[maxn],s,t,fa[maxn],size[maxn],son[maxn],tot;//fa父亲,size它所在子树的点的数目,son重儿子int pathid[maxn],pathtop[maxn],dep[maxn];//pathid在线段树中的位置,pathtop它所在重链的顶端节点,dep深度int sumTree[maxn],markTree[maxn];//sumTree标记第一种操作,markTree标记第二种操作。bool col[maxn];//标记该点上面的轻链是否改变int n,m,a,b,c;inline void add_edge(int a,int b){    ma[a].push_back(b);ma[b].push_back(a);}struct Tree{    int l,r,ls,rs,sum;    bool lz;            //翻转两次就相当于不变}tree[maxn<<2];int build(int l,int r){    int rt = tot++;    tree[rt].l = l;tree[rt].r = r;    tree[rt].lz = tree[rt].sum = 0;    if(l + 1 == r){        tree[rt].ls = tree[rt].rs = -1;        return rt;    }    int mid = l + r >> 1;    tree[rt].ls = build(l,mid);    tree[rt].rs = build(mid,r);    return rt;}inline void resum(int rt){    tree[rt].sum = tree[rt].r - tree[rt].l - tree[rt].sum;}inline void pushdown(int rt){    if(tree[rt].lz){        resum(lc),resum(rc);        tree[lc].lz ^= 1;tree[rc].lz ^= 1;        tree[rt].lz = false;    }}inline void pushup(int rt){    tree[rt].sum = tree[lc].sum + tree[rc].sum;}void update(int rt,int l,int r){    if(r <= l) return ;    if(l >= tree[rt].r || r <= tree[rt].l) return ;    if(tree[rt].l == l && tree[rt].r == r){        tree[rt].lz ^= 1;        tree[rt].sum = tree[rt].r - tree[rt].l - tree[rt].sum;        return ;    }    pushdown(rt);    int mid = tree[rt].l + tree[rt].r >> 1;    if(r <= mid) update(lc,l,r);    else if(l >= mid) update(rc,l,r);    else update(lc,l,mid),update(rc,mid,r);    pushup(rt);}int query(int rt,int l,int r){    if(r <= l ) return 0;    if( l >= tree[rt].r || r <= tree[rt].l) return 0;    if(tree[rt].l == l && tree[rt].r == r) return tree[rt].sum;    pushdown(rt);    int mid = tree[rt].l + tree[rt].r >> 1;    if( r <= mid) return query(lc,l,r);    else if( l >= mid) return query(rc,l,r);    else return query(lc,l,mid) + query(rc,mid,r);}void buildpath(){       //非递归方式的树链剖分    int u,v;    s = 0,t = 0;    que[t++] = 0;    fa[0] = -1;dep[0] = 1;    while(s < t) {        u = que[s++];        rep(i,ma[u].size()) {            v = ma[u][i];            if(v != fa[u]) {                fa[v] = u ; dep[v] = dep[u]+1 ; que[t++] = v;             }        }    }    for(int j=n-1;j>=0;--j){        u = que[j];        son[u] = -1;        size[u] = 1;        rep(i,ma[u].size()){            v = ma[u][i];            if(v != fa[u]){                size[u] += size[v];                if(son[u] == -1 || size[v] > size[son[u]]) son[u] = v;            }        }        if(son[u] == -1) son[u] = u;    }    memset(pathtop,-1,sizeof(pathtop));    for(int i=0;i<n;++i){        u = que[i];        if(pathtop[u] != -1) continue;        int top = u,cnt = 0;        for(;;) {            pathtop[u] = top;            pathid[u] = cnt++;            if(son[u] == u) break;            u = son[u];        }        sumTree[top] = build(0,cnt);        markTree[top] = build(0,cnt);    }}void rev1(int u,int v){    while(pathtop[u] != pathtop[v]){        if(dep[pathtop[u]] < dep[pathtop[v]]) swap(u,v);        update(sumTree[pathtop[u]],1,pathid[u]+1);  //重链的改变        u = pathtop[u];        col[u] ^= 1;    //轻链的改变        u = fa[u];    }    if(pathid[u] > pathid[v]) swap(u,v);    update(sumTree[pathtop[u]],pathid[u]+1,pathid[v]+1);}void rev2(int u,int v){    while(pathtop[u] != pathtop[v]) {        if(dep[pathtop[u]] < dep[pathtop[v]]) swap(u ,v);        update(markTree[pathtop[u]],0,pathid[u]+1);        update(sumTree[pathtop[u]],pathid[u]+1,pathid[u]+2);    //把下面的边变色        u = pathtop[u];        col[u] ^= 1;        //把上面的边变色        u = fa[u];    }    if(pathid[u] > pathid[v]) swap(u,v);    update(markTree[pathtop[u]],pathid[u],pathid[v]+1);    if(u == pathtop[u]) col[u] ^= 1;    //u是重链顶端,则改变的是轻链    else update(sumTree[pathtop[u]],pathid[u],pathid[u]+1); //否则要在重链上改变一下    update(sumTree[pathtop[u]],pathid[v]+1,pathid[v]+2);    //因为重链的最底下肯定是重链,所以可以直接把v下面的变色}int cal(int u){    int f = fa[u];    return col[u] ^ query(markTree[pathtop[f]],pathid[f],pathid[f]+1);  //轻边只和它的标记和它的上面的重边有关}int sum(int u,int v){      int res = 0;    int tp = 10;    while (pathtop[u] != pathtop[v]) {        if(dep[pathtop[u]] < dep[pathtop[v]]) swap(u,v);        res += query(sumTree[pathtop[u]],1,pathid[u]+1);        u = pathtop[u];        res += cal(u);              //轻边只有在这个操作才被加上        u = fa[u];    }    if(pathid[u] > pathid[v]) swap(u,v);    res += query(sumTree[pathtop[u]],pathid[u]+1,pathid[v]+1);    return res;}int main() {    //freopen("input.txt","r",stdin);     int T;    scanf("%d",&T);    while(T--){        scanf("%d",&n);        tot = 0;        rep(i,n) ma[i].clear();        memset(col,false,sizeof(col));        rep(i,n-1){            scanf("%d%d",&a,&b);            a--,b--;            add_edge(a,b);        }        buildpath();            scanf("%d",&m);        while(m--){            scanf("%d%d%d",&a,&b,&c);            b--;c--;            if(a == 1) rev1(b,c);            else if(a == 2) rev2(b,c);            else printf("%d\n",sum(b,c));        }    }    return 0;}


看到一种直接把所有边建到一颗线段树上的代码,思路是一样的,更加简洁。。orz了.

转自:链接
思路是一样的,记录一下~
#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>using namespace std;typedef long long LL;const int MAXV = 100010;const int MAXE = MAXV << 1;const int MAXT = MAXV << 2;int head[MAXV], ecnt;int to[MAXE], next[MAXE];int n, m, T;//Graphvoid initGraph() {    memset(head + 1, -1, n * sizeof(int));    ecnt = 0;}void add_edge(int u, int v) {    to[ecnt] = v; next[ecnt] = head[u]; head[u] = ecnt++;    to[ecnt] = u; next[ecnt] = head[v]; head[v] = ecnt++;}//Segment Tree#define ll (x << 1)#define rr (ll | 1)#define mid ((l + r) >> 1)int sum[MAXT], flip[MAXT];int light[MAXT];//轻边是否翻转void initSegmentTree() {    memset(sum + 1, 0, 4 * n * sizeof(int));    memset(flip + 1, 0, 4 * n * sizeof(int));    memset(light + 1, 0, 4 * n * sizeof(int));}void pushdown(int x, int l, int r) {    if(flip[x]) {        flip[ll] ^= 1;        sum[ll] = (mid - l + 1) - sum[ll];        flip[rr] ^= 1;        sum[rr] = (r - mid) - sum[rr];        flip[x] = 0;    }    if(light[x]) {        light[ll] ^= 1;        light[rr] ^= 1;        light[x] = 0;    }}void maintain(int x) {    sum[x] = sum[ll] + sum[rr];}void modifyFlip(int x, int l, int r, int a, int b) {    if(a <= l && r <= b) {        flip[x] ^= 1;        sum[x] = (r - l + 1) - sum[x];    } else {        pushdown(x, l, r);        if(a <= mid) modifyFlip(ll, l, mid, a, b);        if(mid < b) modifyFlip(rr, mid + 1, r, a, b);        maintain(x);    }}void modifyLight(int x, int l, int r, int a, int b) {    if(a <= l && r <= b) {        light[x] ^= 1;    } else {        pushdown(x, l, r);        if(a <= mid) modifyLight(ll, l, mid, a, b);        if(mid < b) modifyLight(rr, mid + 1, r, a, b);    }}int queryFlip(int x, int l, int r, int a, int b) {    if(a <= l && r <= b) {        return sum[x];    } else {        int res = 0;        pushdown(x, l, r);        if(a <= mid) res += queryFlip(ll, l, mid, a, b);        if(mid < b) res += queryFlip(rr, mid + 1, r, a, b);        return res;    }}int queryLight(int x, int l, int r, int a, int b) {    if(a <= l && r <= b) {        return light[x];    } else {        int res = 0;        pushdown(x, l, r);        if(a <= mid) res += queryLight(ll, l, mid, a, b);        if(mid < b) res += queryLight(rr, mid + 1, r, a, b);        return res;    }}//树链剖分int fa[MAXV], size[MAXV], son[MAXV], top[MAXV], tid[MAXV], dep[MAXV];int dfs_clock;void dfs_size(int u, int f, int depth) {    fa[u] = f; dep[u] = depth;    size[u] = 1; son[u] = 0;    int maxsize = 0;    for(int p = head[u]; ~p; p = next[p]) {        int &v = to[p];        if(v == f) continue;        dfs_size(v, u, depth + 1);        size[u] += size[v];        if(size[v] > maxsize) {            son[u] = v;            maxsize = size[v];        }    }}void dfs_heavy_edge(int u, int ancestor) {    tid[u] = ++dfs_clock; top[u] = ancestor;    if(son[u]) dfs_heavy_edge(son[u], ancestor);    for(int p = head[u]; ~p; p = next[p]) {        int &v = to[p];        if(v == fa[u] || v == son[u]) continue;        dfs_heavy_edge(v, v);    }}void modifyFlip(int a, int b) {    while(top[a] != top[b]) {        if(dep[top[a]] < dep[top[b]]) swap(a, b);        modifyFlip(1, 1, n, tid[top[a]], tid[a]);        a = fa[top[a]];    }    if(a != b) {        if(dep[a] < dep[b]) swap(a, b);        modifyFlip(1, 1, n, tid[son[b]], tid[a]);    }}void modifyLight(int a, int b) {    while(top[a] != top[b]) {        if(dep[top[a]] < dep[top[b]]) swap(a, b);        modifyLight(1, 1, n, tid[top[a]], tid[a]);        if(son[a]) modifyFlip(1, 1, n, tid[son[a]], tid[son[a]]);        modifyFlip(1, 1, n, tid[top[a]], tid[top[a]]);  //翻转轻边        a = fa[top[a]];    }    if(dep[a] < dep[b]) swap(a, b);    modifyLight(1, 1, n, tid[b], tid[a]);    if(fa[b]) modifyFlip(1, 1, n, tid[b], tid[b]);    if(son[a]) modifyFlip(1, 1, n, tid[son[a]], tid[son[a]]);}int query(int a, int b) {    int res = 0;    while(top[a] != top[b]) {        if(dep[top[a]] < dep[top[b]]) swap(a, b);        if(a != top[a]) res += queryFlip(1, 1, n, tid[son[top[a]]], tid[a]);        res += queryFlip(1, 1, n, tid[top[a]], tid[top[a]]) ^ queryLight(1, 1, n, tid[fa[top[a]]], tid[fa[top[a]]]);        a = fa[top[a]];    }    if(a != b) {        if(dep[a] < dep[b]) swap(a, b);        res += queryFlip(1, 1, n, tid[son[b]], tid[a]);    }    return res;}void ask() {    int a, b;    while(cin>>a>>b && a + b) {        cout<<query(a, b)<<endl;    }}int main() {    scanf("%d", &T);    while(T--) {        scanf("%d", &n);        initGraph();        for(int i = 1, a, b; i < n; ++i) {            scanf("%d%d", &a, &b);            add_edge(a, b);        }        initSegmentTree();        dfs_clock = 0;        dfs_size(1, 0, 0);        dfs_heavy_edge(1, 1);        scanf("%d", &m);        for(int i = 0, op, a, b; i < m; ++i) {            scanf("%d%d%d", &op, &a, &b);            if(op == 1) modifyFlip(a, b);            if(op == 2) modifyLight(a, b);            if(op == 3) printf("%d\n", query(a, b));            //if(i == 1) ask();        }    }}



0 0