hdu 4857 Little Devil I

http://acm.hdu.edu.cn/showproblem.php?pid=4897

题意:
给你一棵树,边的颜色要么为白色,要么为黑色,初始每条边为白色,有三种操作

1、将u-v链上面的所有边的颜色翻转

2、将u-v链上面所有邻接的边翻转(边上只有一个点在链上面)

3、询问u->v上面有多少黑色的边

树链剖分,线段树维护4个信息:

按dfs序建立线段树后,如果线段树内节点的区间为[l,r],则此节点维护树上dfs序[l,r]内的父边的信息

父边指 点与父节点之间的边

sum0:节点的父边属于重链且颜色为白色 的边数

sum1:节点的父边属于重链且颜色为黑色 的边数

rev1:节点的父边颜色是否被操作1取反 (实际只会用到属于轻链的边)

rev2:节点的子树中,与节点直接相连的属于轻链边 是否被操作2取反

操作1:直接取反,交换sum0和sum1,维护标记rev1

细节:最后u和v(dep[u]<dep[v])汇集到一条重链的时候,最后一次操作不包括u,因为点代表的是父边的信息

操作2:一条链的相邻边,除了对链上的点维护rev2操作外,

链最顶端的点如果是其父节点的重儿子,需要修改它的rev1

路径上每条重链最底端的点,如果它有重儿子,需要修改它重儿子的rev1

因为标记rev2只维护轻链

操作3:重链直接查,轻链呢?

在树链剖分往上跳的时候,跳轻链一定是只跳一条边

假设这条边连接了节点u和v,dep[u]<dep[v]

如果rev2[u]^rev2[v]^rev1[v] 为 true,则这条边为黑色

clj的题就是好哇!!!

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

#define N 100001

int n;

int front[N],nxt[N<<1],to[N<<1],tot;

int siz[N],dep[N],fa[N];
int bl[N],son[N];
int id[N],dy[N],cnt;

bool big[N];

int sum0[N<<2],sum1[N<<2];
bool rev1[N<<2],rev2[N<<2];

int ans;

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); }
}

void add(int u,int v)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot;
}

void dfs1(int x)
{
    siz[x]=1;
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=fa[x])
        {
            fa[to[i]]=x;
            dep[to[i]]=dep[x]+1;
            dfs1(to[i]);
            siz[x]+=siz[to[i]];
        }
}

void dfs2(int x,int top)
{
    bl[x]=top;
    id[x]=++cnt;
    dy[cnt]=x;
    int y=0;
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=fa[x] && siz[to[i]]>siz[y]) y=to[i];
    if(y)
    {
        son[x]=y;
        big[y]=true;
        dfs2(y,top);
    }
    else return;
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=fa[x] && to[i]!=y) dfs2(to[i],to[i]);
}

void down1(int k)
{
    rev1[k<<1]^=1;
    swap(sum0[k<<1],sum1[k<<1]);
    rev1[k<<1|1]^=1;
    swap(sum0[k<<1|1],sum1[k<<1|1]);
    rev1[k]^=1;
}

void down2(int k)
{
    rev2[k<<1]^=1;
    rev2[k<<1|1]^=1;
    rev2[k]^=1;
}

void Reverse(int k,int l,int r,int opl,int opr,int ty)
{
    if(l>=opl && r<=opr)
    {
        if(ty==1)
        {
            swap(sum1[k],sum0[k]);
            rev1[k]^=1;
        }
        else rev2[k]^=1;
        return;
    }
    int mid=l+r>>1;
    if(rev1[k]) down1(k);
    if(rev2[k]) down2(k);
    if(opl<=mid) Reverse(k<<1,l,mid,opl,opr,ty);
    if(opr>mid) Reverse(k<<1|1,mid+1,r,opl,opr,ty);
    if(ty==1)
    {
        sum1[k]=sum1[k<<1]+sum1[k<<1|1];
        sum0[k]=sum0[k<<1]+sum0[k<<1|1];
    }
}

int get_lca(int u,int v)
{
    while(bl[u]!=bl[v])
    {
        if(dep[bl[u]]<dep[bl[v]]) swap(u,v);
        u=fa[bl[u]];
    }
    return dep[u]<dep[v] ? u : v;
}

bool point_query(int k,int l,int r,int x,int ty)
{
    if(l==r) return ty==1 ? rev1[k] : rev2[k];
    if(rev1[k]) down1(k);
    if(rev2[k]) down2(k);
    int mid=l+r>>1;
    if(x<=mid) return point_query(k<<1,l,mid,x,ty);
    return point_query(k<<1|1,mid+1,r,x,ty);
}

void query(int k,int l,int r,int opl,int opr)
{
    if(l>=opl && r<=opr)
    {
        ans+=sum1[k];
        return;
    }
    if(rev1[k]) down1(k);
    if(rev2[k]) down2(k);
    int mid=l+r>>1;
    if(opl<=mid) query(k<<1,l,mid,opl,opr);
    if(opr>mid) query(k<<1|1,mid+1,r,opl,opr);
}

void solve(int ty,int u,int v)
{
    if(ty==1)
    {
        while(bl[u]!=bl[v])
        {
            if(dep[bl[u]]<dep[bl[v]]) swap(u,v);
            Reverse(1,1,n,id[bl[u]],id[u],1);
            u=fa[bl[u]];
        }
        if(dep[u]>dep[v]) swap(u,v);
        if(u!=v) Reverse(1,1,n,id[u]+1,id[v],1);
    }
    else if(ty==2)
    {
        int lca=get_lca(u,v);
        if(lca!=u && lca!=v)
        {
            if(big[lca]) Reverse(1,1,n,id[lca],id[lca],1);
        }
        else
        {
            if(dep[u]>dep[v]) swap(u,v);
            if(big[u]) Reverse(1,1,n,id[u],id[u],1);
        }
        while(bl[u]!=bl[v])
        {
            if(dep[bl[u]]<dep[bl[v]]) swap(u,v);
            if(son[u]) Reverse(1,1,n,id[son[u]],id[son[u]],1);
            Reverse(1,1,n,id[bl[u]],id[u],2);
            u=fa[bl[u]];
        }
        if(dep[u]>dep[v]) swap(u,v);
        if(son[v]) Reverse(1,1,n,id[son[v]],id[son[v]],1);
        Reverse(1,1,n,id[u],id[v],2);
    }
    else
    {
        ans=0;
        while(bl[u]!=bl[v])
        {
            if(dep[bl[u]]<dep[bl[v]]) swap(u,v);
            query(1,1,n,id[bl[u]],id[u]);
            ans+=point_query(1,1,n,id[bl[u]],2)^point_query(1,1,n,id[fa[bl[u]]],2)^point_query(1,1,n,id[bl[u]],1);
            u=fa[bl[u]];
        }
        if(dep[u]>dep[v]) swap(u,v);
        if(u!=v) query(1,1,n,id[u]+1,id[v]);
        printf("%d\n",ans);
    }
}    

void build(int k,int l,int r)
{
    sum0[k]=sum1[k]=0;
    rev1[k]=rev2[k]=false;
    if(l==r)
    {
        sum0[k]=big[dy[l]];
        return;
    }
    int mid=l+r>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    sum0[k]=sum0[k<<1]+sum0[k<<1|1];
}

void clear()
{
    tot=cnt=0;
    memset(front,0,sizeof(front));
    memset(son,0,sizeof(son));
    memset(big,false,sizeof(big));
}

int main()
{
    freopen("data.in","r",stdin);
    freopen("my.out","w",stdout);
    int T;
    read(T);
    int u,v;
    int ty,m,lca;
    while(T--)
    {
        clear();
        read(n);
        for(int i=1;i<n;++i)
        {
            read(u); read(v);
            add(u,v);
        }
        dfs1(1);
        dfs2(1,1);
        build(1,1,n);
        read(m);
        while(m--)
        {
            read(ty); read(u); read(v);
            solve(ty,u,v);
        }
    }
    return 0;
}

Little Devil I

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

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

Hint

reverse color means change from white to black or vice virsa.

Author

WJMZBMR

原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8456845.html

时间: 2024-10-15 11:00:45

hdu 4857 Little Devil I的相关文章

hdu 4857 逆拓扑+大根堆(priority_queue)

题意:排序输出:在先满足定约束条件下(如 3必需在1前面,7必需在4前面),在满足:1尽量前,其次考虑2,依次.....(即有次约束). 开始的时候,只用拓扑,然后每次在都可以选的时候,优先考虑小的,其实没什么简单,如 图(3-->1,2)这样输出是2.3.1,正确应该是 3 1 2,因为 1要尽量前(都满足第一约束). 参考他人思路结合自己理解:因为这样的弊端就是没有考虑这种情况:图中:若我的"子孙"中,有的比你次优先,虽然现在我们都可以输出,但是要考虑我的子代,若我的子代有次

HDU 4857 Couple doubi(找循环节)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4861 解题报告:桌子上有k个球 ,第i个球的价值wi = 1^i+2^i+...+(p-1)^i (mod p),现在两个人轮流取,如果第一个取的人最后得到的价值总和最大,输出YES,否则 输出NO . k和p的范围都很大,如果都算出来那是不可能的,所以一定是有规律的,所以我把前面的几个打表看了一下,果然有规律,第1.2.3.......(p-2),(p-1)个球的价值分别是: 0,0,0.....

HDU 4857 topological_sort

逃生 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 877 Accepted Submission(s): 236 Problem Description 糟糕的事情发生啦,如今大家都忙着逃命.可是逃命的通道非常窄,大家仅仅能排成一行. 如今有n个人,从1标号到n.同一时候有一些奇怪的约束条件,每一个都形如:a必须在b之前. 同一时候,社会

HDU 4897 Little Devil I

_(:3 ⌒?)_ 调我半天,还是记录下吧. 用轻重链可解决此题. 用轻重链的方式给点重新编号后,建两棵线段树,一棵(sumTree)用于记录路径修改,另外一棵(markTree)用于记录邻边修改的点. 然后维护下两棵树即可. 注意,markTree修改时,要在sumTree上修改第一个点和最后一个点对应的重边,若修改的顶点为连接轻链的点,则sumTree对应边不修改. #include<cstdio> #include<cstring> #include<algorithm

hdu 4857

逃生 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 959    Accepted Submission(s): 284 Problem Description 糟糕的事情发生啦,现在大家都忙着逃命.但是逃命的通道很窄,大家只能排成一行. 现在有n个人,从1标号到n.同时有一些奇怪的约束条件,每个都形如:a必须在b之前.同时,社会是不平

hdu 4857 逃生 (拓扑排序+保证最小在前面)

逃生 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 74    Accepted Submission(s): 13 Problem Description 糟糕的事情发生啦,现在大家都忙着逃命.但是逃命的通道很窄,大家只能排成一行. 现在有n个人,从1标号到n.同时有一些奇怪的约束条件,每个都形如:a必须在b之前. 同时,社会是不平

hdu 4897 Little Devil I(树链剖分+线段树)

题目链接:hdu 4897 Little Devil I 题目大意:给定一棵树,每条边有黑白两种颜色,初始都是白色,现在有三种操作: 1 u v:u到v路径上的边都取成相反的颜色 2 u v:u到v路径上相邻的边都取成相反的颜色(相邻即仅有一个节点在路径上) 3 u v:查询u到v路径上有多少个黑色边 解题思路:树链剖分,用两个线段W和L维护,W对应的是每条的黑白情况,L表示的是每个节点的相邻边翻转情况 (对于轻链而言,重链直接在W上修改) 对于1操作,即为普通的树链剖分,直接在W上修改即可.

HDU 4857 (反向拓扑排序 + 优先队列)

题意:有N个人,M个优先级a,b表示a优先于b,并且每个人有个编号的优先级,输出顺序. 思路来自:与PKU3687一样 在基本的拓扑排序的基础上又增加了一个要求:编号最小的节点要尽量排在前面:在满足上一个条件的基础上,编号第二小的节点要尽量排在前面:在满足前两个条件的基础上,编号第三小的节点要尽量排在前面--依此类推.(注意,这和字典序是两回事,不可以混淆.) 如图 1 所示,满足要求的拓扑序应该是:6 4 1 3 9 2 5 7 8 0. 图 1 一个拓扑排序的例子 一般来说,在一个有向无环图

HDU 4857 拓扑排序 优先队列

n个数,已经有大小关系,现给m个约束,规定a在b之前,剩下的数要尽可能往前移.输出序列 大小关系显然使用拓扑结构,关键在于n个数本身就有大小关系,那么考虑反向建图,优先选择值最大的入度为零的点,这样得到的序列就是从大到小的,最后倒序输出就行了. 写这题的时候头好痛阿肚子好痛阿,再也不想熬夜了,一点效率都没有. /** @Date : 2017-09-29 19:29:12 * @FileName: HDU 4857 拓扑排序 + 优先队列.cpp * @Platform: Windows * @