【BZOJ-4353】Play with tree 树链剖分

4353: Play with tree

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 31  Solved: 19
[Submit][Status][Discuss]

Description

给你一棵包含N个节点的树,设每条边一开始的边权为0,现在有两种操作:

1)给出参数U,V,C,表示把U与V之间的路径上的边权变成C(保证C≥0)

2)给出参数U,V,C,表示把U与V之间的路径上的边权加上C。但是如果U至V之间路径某条边的边权加上C小于0,那么C=这条边的边权的相反数。

你需要统计出每次一操作过后树中边权为0的边有多少条。

Input

第一行两个整数N,M,分别表示表示节点个数与操作数。

接下来N-1行每行两个整数X,Y表示X,Y之间有一条边。

接下来M行每行4个整数P,U,V,C,P表示操作类型,U,V,C的意义见题目描述。

Output

输出文件包括M行,每行一个整数,表示边权为0的边的个数。

Sample Input

5 4
1 2
1 3
2 4
2 5
1 4 5 1
2 5 3 1
2 5 1 -2
1 4 3 0

Sample Output

2
0
1
3

HINT

N, M≤100,000

Source

Solution

就是两个操作:1.树链覆盖C 2.树链+C,如果有边+C<0 则C=-minx

随便链剖一下+线段树维护一下就可以了

维护最小值minx,最小值个数minn

覆盖的时候直接覆盖,+C的时候先查询minx再看看是否修改C,然后+C...输出答案就是,如果minx=0,ans+=minn....

边权的树链剖分,就是把边权下放至点权

边<u,v>的值,由u,v中较深的点保存,这样显然除了root每条边和每个点是一一对应的.

这里查询所有边的值,因为默认1为root,所以直接查询[2,N]的就可以了。修改的时候稍微做一下修改就可以了

所以说随便写写就A了.

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘||ch>‘9‘) {if(ch==‘-‘) f=-1; ch=getchar();}
    while (ch>=‘0‘&&ch<=‘9‘) {x=10*x+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define MAXN 100010
struct EdgeNode{int next,to;}edge[MAXN<<1];
int head[MAXN],cnt=1;
void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);}
int N,M;
#define INF 0x7fffffff
namespace SegmentTree
{
    struct SegmentTreeNode{int l,r,size,minx,minn,tag,del;}tree[MAXN<<2];
    #define ls now<<1
    #define rs now<<1|1
    inline void Update(int now)
    {
        tree[now].minx=min(tree[ls].minx,tree[rs].minx);
        tree[now].minn=tree[ls].minx<tree[rs].minx? tree[ls].minn:tree[rs].minn;
        if (tree[ls].minx==tree[rs].minx) tree[now].minx=tree[ls].minx,tree[now].minn=tree[ls].minn+tree[rs].minn;
    }
    inline void PushDown(int now)
    {
        if (tree[now].del!=-1)
            {
                tree[ls].minx=tree[now].del; tree[ls].minn=tree[ls].size; tree[ls].del=tree[now].del; tree[ls].tag=0;
                tree[rs].minx=tree[now].del; tree[rs].minn=tree[rs].size; tree[rs].del=tree[now].del; tree[rs].tag=0;
                tree[now].del=-1;
            }
        if (tree[now].tag)
            {
                tree[ls].minx+=tree[now].tag; tree[ls].tag+=tree[now].tag;
                tree[rs].minx+=tree[now].tag; tree[rs].tag+=tree[now].tag;
                tree[now].tag=0;
            }
    }
    inline void BuildTree(int now,int l,int r)
    {
        tree[now].l=l,tree[now].r=r,tree[now].size=r-l+1;
        tree[now].del=-1; tree[now].tag=0;
        if (l==r) {tree[now].minn=1; tree[now].minx=0; return;}
        int mid=(l+r)>>1;
        BuildTree(ls,l,mid);
        BuildTree(rs,mid+1,r);
        Update(now);
    }
    inline void Change(int now,int L,int R,int C)
    {
        int l=tree[now].l,r=tree[now].r;
        if (L<=l && R>=r) {tree[now].tag+=C; tree[now].minx+=C; return;}
        PushDown(now);
        int mid=(l+r)>>1;
        if (L<=mid) Change(ls,L,R,C);
        if (R>mid) Change(rs,L,R,C);
        Update(now);
    }
    inline void Modify(int now,int L,int R,int C)
    {
        int l=tree[now].l,r=tree[now].r;
        if (L<=l && R>=r) {tree[now].del=C; tree[now].tag=0; tree[now].minx=C; tree[now].minn=tree[now].size; return;}
        PushDown(now);
        int mid=(l+r)>>1;
        if (L<=mid) Modify(ls,L,R,C);
        if (R>mid) Modify(rs,L,R,C);
        Update(now);
    }
    inline int Getmin(int now,int L,int R)
    {
        int l=tree[now].l,r=tree[now].r;
        if (L<=l && R>=r) return tree[now].minx;
        PushDown(now);
        int mid=(l+r)>>1,re=INF;
        if (L<=mid) re=min(re,Getmin(ls,L,R));
        if (R>mid) re=min(re,Getmin(rs,L,R));
        return re;
    }
    inline int Query(int now,int L,int R)
    {
        int l=tree[now].l,r=tree[now].r;
        if (L<=l && R>=r) return tree[now].minx==0? tree[now].minn:0;
        PushDown(now);
        int mid=(l+r)>>1,re=0;
        if (L<=mid) re+=Query(ls,L,R);
        if (R>mid) re+=Query(rs,L,R);
        return re;
    }
}
namespace ChainPartition
{
    int size[MAXN],fa[MAXN],deep[MAXN],top[MAXN],pl[MAXN],pr[MAXN],pre[MAXN],dfn,son[MAXN];
    void DFS_1(int now)
    {
        size[now]=1;
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=fa[now])
                {
                    deep[edge[i].to]=deep[now]+1;
                    fa[edge[i].to]=now;
                    DFS_1(edge[i].to);
                    size[now]+=size[edge[i].to];
                    if (size[son[now]]<size[edge[i].to]) son[now]=edge[i].to;
                }
    }
    void DFS_2(int now,int chain)
    {
        top[now]=chain; pl[now]=++dfn; pre[dfn]=now;
        if (son[now]) DFS_2(son[now],chain);
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=fa[now] && edge[i].to!=son[now])
                DFS_2(edge[i].to,edge[i].to);
        pr[now]=dfn;
    }
    inline int Get(int x,int y)
    {
        int re=INF;
        while (top[x]!=top[y])
            {
                if (deep[top[x]]<deep[top[y]]) swap(x,y);
                re=min(re,SegmentTree::Getmin(1,pl[top[x]],pl[x]));
                x=fa[top[x]];
            }
        if (deep[x]>deep[y]) swap(x,y);
        if (x!=y) re=min(re,SegmentTree::Getmin(1,pl[x]+1,pl[y]));
        return re;
    }
    inline void Change(int x,int y,int C)
    {
        int minx=Get(x,y);
        if (minx+C<0) C=-minx;
//        printf("Change   %d %d %d\n",x,y,C);
        while (top[x]!=top[y])
            {
                if (deep[top[x]]<deep[top[y]]) swap(x,y);
                SegmentTree::Change(1,pl[top[x]],pl[x],C);
                x=fa[top[x]];
            }
        if (deep[x]>deep[y]) swap(x,y);
        if (x!=y) SegmentTree::Change(1,pl[x]+1,pl[y],C);
        printf("%d\n",SegmentTree::Query(1,2,N));
    }
    inline void Modify(int x,int y,int C)
    {
//        printf("Modify   %d %d %d\n",x,y,C);
        while (top[x]!=top[y])
            {
                if (deep[top[x]]<deep[top[y]]) swap(x,y);
                SegmentTree::Modify(1,pl[top[x]],pl[x],C);
                x=fa[top[x]];
            }
        if (deep[x]>deep[y]) swap(x,y);
        if (x!=y) SegmentTree::Modify(1,pl[x]+1,pl[y],C);
        printf("%d\n",SegmentTree::Query(1,2,N));
    }
}
int main()
{
    N=read(),M=read();
    for (int x,y,i=1; i<=N-1; i++) x=read(),y=read(),InsertEdge(x,y);
    ChainPartition::DFS_1(1); ChainPartition::DFS_2(1,1);
    SegmentTree::BuildTree(1,1,N);
    while (M--)
        {
            int opt=read(),u=read(),v=read(),C=read();
            switch (opt)
                {
                    case 1: ChainPartition::Modify(u,v,C); break;
                    case 2: ChainPartition::Change(u,v,C); break;
                }
        }
    return 0;
}

以前看蛋蛋写了好几天....还找Claris要代码.....

1h左右就A了....不过无故弄了个本题第一个RE

ShallWe:"这不是傻逼题么?"

时间: 2024-10-08 14:47:20

【BZOJ-4353】Play with tree 树链剖分的相关文章

SPOJ375 Query on a tree 树链剖分

SPOJ375  Query on a tree   树链剖分 no tags You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of

SPOJ - QTREE 375 Query on a tree 树链剖分+线段树

操作1:修改第k条边权. 操作2:询问两点间最大边权. 树链剖分,然后线段树维护最大值 #include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<set> #include<map> #include<queue> #include<vector> #inclu

Aizu 2450 Do use segment tree 树链剖分+线段树

Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show.php?pid=39566 Description Given a tree with n (1 ≤ n ≤ 200,000) nodes and a list of q (1 ≤ q ≤ 100,000) queries, process the queries in order and out

spoj 375 Query on a tree (树链剖分)

Query on a tree You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of the i-th edge to ti or Q

bzoj 3531 [Sdoi2014]旅行(树链剖分,线段树)

3531: [Sdoi2014]旅行 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 876  Solved: 446[Submit][Status][Discuss] Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们用不同的正整数代表 各种宗教,  S国的居民常常旅行.旅行时他们总

SPOJ Query on a tree 树链剖分 水题

You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of the i-th edge to tior QUERY a b : ask fo

hdu 4912 Paths on the tree(树链剖分+贪心)

题目链接:hdu 4912 Paths on the tree 题目大意:给定一棵树,和若干个通道,要求尽量选出多的通道,并且两两通道不想交. 解题思路:用树链剖分求LCA,然后根据通道两端节点的LCA深度排序,从深度最大优先选,判断两个节点均没被标 记即为可选通道.每次选完通道,将该通道LCA以下点全部标记. #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include

poj 3237 Tree 树链剖分+线段树

Description You are given a tree with N nodes. The tree's nodes are numbered 1 through N and its edges are numbered 1 through N ? 1. Each edge is associated with a weight. Then you are to execute a series of instructions on the tree. The instructions

BZOJ 3626 LNOI 2014 LCA 树链剖分

题目大意:给出一棵树,有n个问题,询问在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和. 思路:不会,然后看了题解,之后发现自己智商严重不足. 看到数据范围就知道一定要离线处理,就这个离线处理我估计以我的智商不看题解是肯定想不出来的.. 考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案.观察到,深度其实就是上面有几个已标记了的点(包括自身).所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r