BZOJ 3307 雨天的尾巴 线段树

题目大意:给定一棵树,有m个操作,每次给一条路径上的每个点分发一个颜色为z的物品,所有操作结束后输出每个点上数量最多的是哪种物品

对于每个操作,我们在x和y上各打上一个插入z的标记,然后在LCA(x,y)和Fa(LCA(x,y))上各打上一个删除z的标记

然后我们对z维护线段树

DFS一遍,对于每个节点进行如下操作:

1.将所有子节点的线段树合并过来

2.处理标记,该插♂入的插♂入,该删除的删除

3.查询最大值作为答案

这样的复杂度是O(mlogm)的

然而跑不过树链剖分什么鬼……是我没离散化的关系?

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;

struct Segtree{
    Segtree *ls,*rs;
    int pos,val;
    void* operator new (size_t)
    {
        static Segtree *mempool,*C;
        if(C==mempool)
            mempool=(C=new Segtree[1<<15])+(1<<15);
        C->ls=C->rs=0x0;
        C->pos=0;C->val=0;
        return C++;
    }
    void Push_Up()
    {
        val=0;
        if(ls&&ls->val>val)
            val=ls->val,pos=ls->pos;
        if(rs&&rs->val>val)
            val=rs->val,pos=rs->pos;
    }
    friend void Modify(Segtree *&p,int x,int y,int pos,int val)
    {
        int mid=x+y>>1
        if(!p) p=new Segtree;
        if(x==y)
        {
            p->pos=mid;
            p->val+=val;
            if(!p->val)
                p=0x0;
            return ;
        }
        if(pos<=mid)
            Modify(p->ls,x,mid,pos,val);
        else
            Modify(p->rs,mid+1,y,pos,val);
        p->Push_Up();
        if(!p->val)
            p=0x0;
    }
    friend void Merge(Segtree *&p1,Segtree *p2,int x,int y)
    {
        int mid=x+y>>1;
        if(!p2) return ;
        if(!p1)
        {
            p1=p2;
            return ;
        }
        if(x==y)
        {
            p1->val+=p2->val;
            return ;
        }
        Merge(p1->ls,p2->ls,x,mid);
        Merge(p1->rs,p2->rs,mid+1,y);
        p1->Push_Up();
    }
}*tree[M];

struct abcd{
    int to,next;
}table[M<<1];

int n,m;
int dpt[M],fa[M][17],ans[M];

vector<int> operations;

void Add(int x,int y)
{
    table[++tot].to=y;
    table[tot].next=head[x];
    head[x]=tot;
}

void DFS1(int x)
{
    int i,j;
    dpt[x]=dpt[fa[x][0]]+1;
    for(j=1;j<=16;j++)
        fa[i][j]=fa[fa[i][j-1]][j-1];
    for(i=head[x];i;i=table[i].next)
        if(table[i].to!=fa[x])
            fa[table[i].to][0]=x,DFS1(table[i].to);
}

int LCA(int x,int y)
{
    int j;
    for(j=16;~j;j--)
        if(dpt[fa[x][j]]>=dpt[y])
            x=fa[x][j];
    if(x==y) return x;
    for(j=16;~j;j--)
        if(fa[x][j]!=fa[y][j])
            x=fa[x][j],y=fa[y][j];
    return fa[x][0];
}

void DFS2(int x)
{
    int i;
    for(i=head[x];i;i=table[i].next)
        if(table[i].to!=fa[x][0])
        {
            DFS2(table[i].to);
            Merge(tree[x],tree[table[i].to]);
        }
    vector<int>::iterator it;
    for(it=operations[x].begin();it!=operations[x].end();it++)
        if(*it>0)
            Modify(tree[x],1,1000000000,*it,1);
        else
            Modify(tree[x],1,1000000000,-*it,-1);
    if(tree[x])
        ans[x]=tree[x]->pos;
}

int main()
{
    int i,x,y,z;
    cin>>n>>m;
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        Add(x,y);Add(y,x);
    }
    DFS1(1);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        int lca=LCA(x,y);
        operations[x].push_back(z);
        operations[y].push_back(z);
        operations[lca].push_back(-z);
        operations[fa[lca][0]].push_back(-z);
    }
    DFS2(1);
    for(i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}
时间: 2024-08-07 08:37:15

BZOJ 3307 雨天的尾巴 线段树的相关文章

luogu4556 雨天的尾巴 (线段树合并+差分)

题目背景 深绘里一直很讨厌雨天.灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切.虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉.无奈的深绘里和村民们只好等待救济粮来维生.不过救济粮的发放方式很特别.题目描述 首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮.然后深绘里想知道,当所有的救济粮发

bzoj 3307 雨天的尾巴

题目链接:传送门 题目大意:中文题,略 题目思路:网上有题解说是合并线段树的,但是太难蒟蒻不会,只能用树剖求解 如果不是树而是一维数组我们会怎么解? 当然是利用前缀和思想标记 (L) v+1,(R+1) v-1,然后扫一遍 用线段树取最大复杂度 nlogn 现在是搬到了树上,怎么做? 利用树链剖分拆成链然后在像一维那样做就行,复杂度n(logn)^2 1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib&g

P4556 雨天的尾巴 线段树合并

使用线段树合并,每个节点维护一棵权值线段树,下标为救济粮种类,区间维护数量最多的救济粮编号(下标).所以每个节点答案即为\(tre[rot[x]]\). 然后运用树上点的差分思想,对于分发路径\(u,v\),我们在\(u\)上+1,在\(v\)+1,在\(lca(u,v)\)处-1,在\(fa(lca)\)处-1,最后统计时自底向上做树上前缀和.线段树合并即得当前节点信息. 需要注意的是,在合并时可能会出现\(tre[rot[x]]\)不为\(0\),但是\(sum[rot[x]]\)为\(0\

BZOJ 1835 基站选址(线段树优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1835 题意:有N个村庄坐落在一条直线上,第 i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村 庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位 置,使得总费用最小. 思路: 另外,程序中的n=n+1,m=

BZOJ 3211 花神游历各国 线段树题解

BZOJ 3211 花神游历各国 线段树题解 3211: 花神游历各国 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 2551  Solved: 946[Submit][Status][Discuss] Description Input Output 每次x=1时,每行一个整数,表示这次旅行的开心度 Sample Input 4 1 100 5 5 5 1 1 2 2 1 2 1 1 2 2 2 3 1 1 4 Sample Output 101

GDOI模拟雨天的尾巴【树套树】

雨天的尾巴 深绘里一直很讨厌雨天.灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切.虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉.无奈的深绘里和村民们只好等待救济粮来维生.不过救济粮的发放方式很特别.首先村落里的一共有n 座房屋,并形成一个树状结构.然后救济粮分m 次发放,每次选择两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮.然后深绘里想知道,当所有的救

[BZOJ 3196] 213平衡树 【线段树套set + 树状数组套线段树】

题目链接:BZOJ - 3196 题目分析 区间Kth和区间Rank用树状数组套线段树实现,区间前驱后继用线段树套set实现. 为了节省空间,需要离线,先离散化,这样需要的数组大小可以小一些,可以卡过128MB = = 嗯就是这样,代码长度= =我写了260行......Debug了n小时= = 代码 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #in

BZOJ 4059 Cerc2012 Non-boring sequences 线段树+扫描线

题目大意:定义一个序列为[不无聊的]当且仅当这个序列的任意一个区间都存在一个数只出现过一次,给定一个序列,要求判断这个序列是否是[不无聊的] 定义lasti表示第i个元素上一次出现的位置(第一次出现则为0),nexti表示第i个元素下一次出现的位置(最后一次出现则为n+1),那么这个元素能成为某个区间仅出现一次的数,当且仅当这个区间的左端点在[lasti+1,i]之间,右端点在[i,nexti?1]之间 我们可以将区间的左右端点放在二维平面上,那么一个元素产生的贡献是一个矩形,我们要确定的是所有

BZOJ 3196 二逼平衡树 线段树+treap

题意:链接 方法:线段树+treap的模板题 题解: 首先是对于整个树的定义,其实treap部分并没有什么区别,只不过是单root改变为多root而已. #include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define lson l,mid,rt<<1 #define rson mid+1,