zoukankan      html  css  js  c++  java
  • 【BZOJ3589】动态树 树链剖分+线段树

    Description

    别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件

    事件0:

    这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子.

    事件1:

    小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次.

    Input

    第一行一个整数n(1<=n<=200,000), 即节点数.

    接下来n-1行, 每行两个数字u, v. 表示果子u和果子v之间有一条直接的边. 节点从1开始编号.

    在接下来一个整数nQ(1<=nQ<=200,000), 表示事件.

    最后nQ行, 每行开头要么是0, 要么是1.

    如果是0, 表示这个事件是事件0. 这行接下来的2个整数u, delta表示以u为根的子树中的每个节点长出了delta个果子.

    如果是1, 表示这个事件是事件1. 这行接下来一个整数K(1<=K<=5), 表示这次询问涉及K个树枝. 接下来K对整数u_k, v_k, 每个树枝从节点u_k到节点v_k. 由于果子数可能非常多, 请输出这个数模2^31的结果.

    Output

    对于每个事件1, 输出询问的果子数.

    Sample Input

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

    Sample Output

    13

    HINT

    1 <= n <= 200,000, 1 <= nQ <= 200,000, K = 5.

    生成每个树枝的过程是这样的:先在树中随机找一个节点, 然后在这个节点到根的路径上随机选一个节点, 这两个节点就作为树枝的两端.

    Sol

    这题为啥要容斥啊,不就一线段树+树剖题吗。(不过BIT的log*32确实比树剖的俩log*5快)

    我们在线段树中维护当前的总和sum以及实际有效的值val,每次查询的时候,把树链上面的点系数改成1,之后获得答案,然后再把整棵树的系数改成0。

    写完之后居然没调就过了样例???(逃

    Code

    #include <bits/stdc++.h>
    #define mid ((s[x].l+s[x].r)>>1)
    using namespace std;
    struct seg{int l,r,sum,val,tag,lzy;}s[2000005];
    int n,x,y,op,m,z,I,fa[400005],dep[400005],top[400005],son[400005],siz[400005],id[400005];
    vector<int>e[400005];
    int dfs1(int x)
    {
    	int maxx=0,tot=0;
    	for(int i=0;i<e[x].size();i++) if(e[x][i]!=fa[x])
    	{
    		dep[e[x][i]]=dep[x]+1;fa[e[x][i]]=x;tot+=dfs1(e[x][i]);
    		if(siz[e[x][i]]>maxx) maxx=siz[e[x][i]],son[x]=e[x][i];
    	}
    	return siz[x]=tot+1;
    }
    void dfs2(int x,int Fa)
    {
    	id[x]=++I;top[x]=Fa;
    	if(son[x]) dfs2(son[x],Fa);
    	for(int i=0;i<e[x].size();i++) if(e[x][i]!=fa[x]&&e[x][i]!=son[x]) dfs2(e[x][i],e[x][i]);
    }
    void build(int x,int L,int R)
    {
    	s[x]=(seg){L,R,0,0,-1,0};
    	if(L==R) return;
    	build(x*2,L,mid);build(x*2+1,mid+1,R);
    }
    void down(int x)
    {
    	s[x*2].sum+=s[x].lzy*(s[x*2].r-s[x*2].l+1);s[x*2+1].sum+=s[x].lzy*(s[x*2+1].r-s[x*2+1].l+1);
    	s[x*2].lzy+=s[x].lzy;s[x*2+1].lzy+=s[x].lzy;s[x].lzy=0;
    	if(s[x].tag==-1) return;
    	s[x*2].val=s[x].tag*s[x*2].sum;s[x*2+1].val=s[x].tag*s[x*2+1].sum;
    	s[x*2].tag=s[x*2+1].tag=s[x].tag;s[x].tag=-1;
    }
    void update(int x,int L,int R,int V,int O)
    {
    	down(x);
    	if(L<=s[x].l&&s[x].r<=R)
    	{
    		if(!O) s[x].val=s[x].sum*V,s[x].tag=V;
    		else s[x].lzy+=V,s[x].sum+=V*(s[x].r-s[x].l+1);
    		return; 
    	}
    	if(L<=mid) update(x*2,L,R,V,O);if(R>mid) update(x*2+1,L,R,V,O);
    	s[x].sum=s[x*2].sum+s[x*2+1].sum;s[x].val=s[x*2].val+s[x*2+1].val;
    }
    void solve(int x,int y)
    {
    	for(;top[x]!=top[y];update(1,id[top[x]],id[x],1,0),x=fa[top[x]]) if(dep[top[x]]<dep[top[y]]) swap(x,y);
    	if(dep[x]>dep[y]) swap(x,y);update(1,id[x],id[y],1,0);
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<n;i++) scanf("%d%d",&x,&y),e[x].push_back(y),e[y].push_back(x);
    	dfs1(1);dfs2(1,1);build(1,1,n);
    	for(scanf("%d",&m);m--;)
    	{
    		scanf("%d",&op);
    		if(!op){scanf("%d%d",&x,&y),update(1,id[x],id[x]+siz[x]-1,y,1);continue;}
    		for(scanf("%d",&z);z--;) scanf("%d%d",&x,&y),solve(x,y);
    		printf("%d
    ",s[1].val&0x7fffffff);update(1,1,n,0,0);
    	}
    }
    
  • 相关阅读:
    .Net常用的命名空间
    Jquery测试纠错笔记
    第一章 学习总结
    Java和C++引用的区别
    gin的墙内开发艺术
    golang几个环境变量的问题
    Leetcode240_搜索二维矩阵II
    Leetcode1358_包含所有三种字符的子字符串数目
    Leetcode1354_多次求和构造目标数组
    Leetcode1353_最多可以参加的会议数目
  • 原文地址:https://www.cnblogs.com/CK6100LGEV2/p/9413109.html
Copyright © 2011-2022 走看看