洛谷P4556 雨天的尾巴 线段树

正解:线段树合并

解题报告:

传送门!

考虑对树上的每个节点开一棵权值线段树,动态开点,记录一个max(num,id)(这儿的id,define了一下,,,指的是从小到大排QAQ

然后修改操作可以考虑树上差分,大概形式就tr[l]++,tr[r]++,tr[lca]--,tr[lca.fa]--

然后最后求和的时候从底向上合并一边合并一边输出就好

然后这题是有点儿卡空间的(但是我开始学的时候就学的是比较节省空间的那种,,,所以其实并麻油卡住我23333

但是还是总结下线段树合并的几个比较常见的省空间操作

1)空间回收

不难想到在合并两棵树的时候,会有一棵树在合并之后就麻油用了嘛,就很浪费那些点

所以我们可以优先考虑重复利用不再会被用到的节点

所以另外开一个数组,存能被重新利用的节点的编号

具体代码看下面趴并不难理解不说了QAQ

但是这题里好像并没有什么用,,,?因为

inline int nw(){if(tot)return rab[tot--];return ++cnt;}
inline void throw(int x){rab[++top]=x,ls[x]=rs[x]=Max[x]=id[x]=0;}
View Code

2)然后还有一个技巧,是只针对这题的,其他题目不一定能用

就是考虑到这题里面合并之后就没有再修改之类的操作了

所以可以直接合并到其中一棵树上

但是如果,在合并操作之后还要支持修改,就要新开点了QAQ

3)还有一个是我自己的小习惯

这个应该一般人不会有这个问题,,,就是我喜欢在结构体里存储l和r

这样确实比较方便

但是!

耗空间阿!

这题我开始MLE了就是这个问题QAQ

没了,我目前get了的就这几个QwQ!

 

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define fr first
#define sc second
#define rg register
#define gc getchar()
#define mp make_pair
#define t(i) edge[i].to
#define rp(i,x,y) for(rg int i=x;i<=y;++i)
#define my(i,x,y) for(rg int i=x;i>=y;--i)
#define e(i,x) for(rg int i=head[x];i;i=edge[i].nxt)

const int N=100000+100;
int n,m,rt[N],nod_cnt,head[N],ed_cnt,fa[N][20],dep[N],as[N],x[N],y[N],z[N],mx;
struct node{int ls,rs,val,mx;}tr[N*50];
struct ed{int to,nxt;}edge[N<<1];

il int read()
{
    rg char ch=gc;rg int x=0;rg bool y=1;
    while(ch!='-' && (ch>'9' || ch<'0'))ch=gc;
    if(ch=='-')ch=gc,y=0;
    while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
    return y?x:-x;
}
il void ad(int x,int y){edge[++ed_cnt]=(ed){x,head[y]};head[y]=ed_cnt;}
il void dfs(int x,int fat){fa[x][0]=fat;dep[x]=dep[fat]+1;rp(i,1,18)fa[x][i]=fa[fa[x][i-1]][i-1];e(i,x)if(t(i)^fat)dfs(t(i),x);}
il int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    my(i,18,0)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];if(x==y)return x;
    my(i,18,0)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
il void pushup(int x)
{if(tr[tr[x].ls].val>=tr[tr[x].rs].val)tr[x].mx=tr[tr[x].ls].mx,tr[x].val=tr[tr[x].ls].val;else tr[x].mx=tr[tr[x].rs].mx,tr[x].val=tr[tr[x].rs].val;}
il void modify(int l,int r,int x,int to,int dat)
{
    if(l==r){tr[x].val+=dat,tr[x].mx=(tr[x].val>0?l:0);return;}
    int mid=(l+r)>>1;
    if(to<=mid){if(!tr[x].ls)tr[x].ls=++nod_cnt;modify(l,mid,tr[x].ls,to,dat);}
    else{if(!tr[x].rs)tr[x].rs=++nod_cnt;modify(mid+1,r,tr[x].rs,to,dat);}
    pushup(x);
}
il int merge(int l,int r,int nw1,int nw2)
{
    if(!nw1 || !nw2)return tr[nw1+nw2].val=tr[nw1].val+tr[nw2].val,tr[nw1+nw2].mx=tr[nw1].mx+tr[nw2].mx,nw1+nw2;
    if(l==r)return tr[nw1].val+=tr[nw2].val,tr[nw1].mx=(tr[nw1].val>0?l:0),nw1;
    int mid=(l+r)>>1;tr[nw1].ls=merge(l,mid,tr[nw1].ls,tr[nw2].ls);tr[nw1].rs=merge(mid+1,r,tr[nw1].rs,tr[nw2].rs);
    pushup(nw1);return nw1;
}
il void dfsdfs(int x,int fat){e(i,x)if(t(i)^fat)dfsdfs(t(i),x),rt[x]=merge(1,mx,rt[x],rt[t(i)]);as[x]=tr[rt[x]].mx;}

int main()
{
    n=read();m=read();rp(i,1,n)rt[i]=++nod_cnt;
    rp(i,1,n-1){int x=read(),y=read();ad(x,y);ad(y,x);}dfs(1,0);
    rp(i,1,m)x[i]=read(),y[i]=read(),z[i]=read(),mx=max(mx,z[i]);
    rp(i,1,m){int lcaa=lca(x[i],y[i]);modify(1,mx,rt[x[i]],z[i],1);modify(1,mx,rt[y[i]],z[i],1);modify(1,mx,rt[lcaa],z[i],-1);if(fa[lcaa][0])modify(1,mx,rt[fa[lcaa][0]],z[i],-1);}
    dfsdfs(1,0);rp(i,1,n)printf("%d\n",as[i]);
    return 0;
}
然后就放代码辣辣辣!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值