学习笔记--线段树合并

先膜一发xhk大佬,线段树合并之前觉得没什么用,经过xhk大佬讲了一波发现用来处理一些有关树的问题很好用,复杂度也不错(应该是nlog?但是因为常数十分爆炸其实跟nlog^2也差不多。。)

例1:Noip2016 天天爱跑步

https://www.luogu.org/problemnew/show/P1600

将链u->v拆成u->lca和lca->v,然后在u,v打Add标记,在lca和lca父亲分别打Del标记(这是为了防止lca被统计两次),然后从下到上把线段树合并起来即可。

#pragma comment(linker, "/STACK:102400000,102400000")
#include
using namespace std;
const int N=3e5+10;
const int M=20;

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

struct SEG{
    int ls,rs,w;
}seg[N*40];
int snum,n,m,a[N];
int dep[N],jp[N][M],rt[N],ans[N];
vectormp[N],add[N],del[N];

void dfs1(int pos,int fa,int deep)
{//cerr<=0;i--)
        if(dep[jp[x][i]]>=dep[y])x=jp[x][i];
    if(x==y)return x;
    for(int i=M-1;i>=0;i--)
        if(jp[x][i]!=jp[y][i])x=jp[x][i],y=jp[y][i];
    return jp[x][0];
}

void ad(int u,int l,int v)
{
    add[u].push_back(dep[u]),del[jp[l][0]].push_back(dep[u]);
    add[v].push_back(2*dep[l]-dep[u]),del[l].push_back(2*dep[l]-dep[u]);
}
int new_node()
{++snum,seg[snum].ls=seg[snum].rs=seg[snum].w=0;return snum;}
void push_up(int x)
{seg[x].w=seg[seg[x].ls].w+seg[seg[x].rs].w;}
int build(int l,int r,int to)
{
    int nw=new_node(),mid;
    if(l==r)seg[nw].w++;
    else
    {
        mid=(l+r)>>1;
        if(to<=mid)seg[nw].ls=build(l,mid,to);
        else seg[nw].rs=build(mid+1,r,to);
        push_up(nw);
    }
    return nw;
}
void ins(int nw,int l,int r,int to)
{
    int mid;
    if(l==r)seg[nw].w++;
    else
    {
        mid=(l+r)>>1;
        if(to<=mid)
        {
            if(!seg[nw].ls)seg[nw].ls=new_node();
            ins(seg[nw].ls,l,mid,to);
        }
        else
        {
            if(!seg[nw].rs)seg[nw].rs=new_node();
            ins(seg[nw].rs,mid+1,r,to);
        }
        push_up(nw);
    }
}
int mg(int x,int y)
{
    if(!x||!y)return x+y;
    seg[x].w=seg[x].w+seg[y].w;
    if(seg[x].ls||seg[y].ls)seg[x].ls=mg(seg[x].ls,seg[y].ls);
    if(seg[x].rs||seg[y].rs)seg[x].rs=mg(seg[x].rs,seg[y].rs);
    return x;
}
void Del(int nw,int l,int r,int to)
{
    int mid;
    if(l==r)seg[nw].w--;
    else
    {
        mid=(l+r)>>1;
        if(to<=mid) Del(seg[nw].ls,l,mid,to);
        else Del(seg[nw].rs,mid+1,r,to);
        push_up(nw);
    }
}
int ask(int nw,int l,int r,int to)
{
    if(l==r&&to==l)return seg[nw].w;
    else
    {
        int mid=(l+r)>>1;
        if(to<=mid&&seg[nw].ls)return ask(seg[nw].ls,l,mid,to);
        if(to>mid&&seg[nw].rs)return ask(seg[nw].rs,mid+1,r,to);
        return 0;
    }
}
void dfs2(int pos,int fa)
{
    for(int i=0;i

例2:[USACO18OPEN]Disruption

https://www.luogu.org/problemnew/show/P4374

线段树合并,对于每个点查询该点之下能到dfs序在该点子树外的点的最小边权即可。

ps:这道题线段树合并nlog在1e5数据范围下因为常数巨大跑的反而比树剖+线段树这种nlog^2但常数小的做法慢。。。不过毕竟严格从复杂度来说还是更优的(自我安慰)

#pragma GCC optimize(2)
#include
#define pii pair
#define fi first
#define sc second
using namespace std;
const int N=1e5+10;
const int inf=2e9;

void read(int &x)
{
    char c=getchar();x=0;bool f=0;
    while(!isdigit(c))f|=(c=='-'),c=getchar();
    while(isdigit(c))x=x*10+c-48,c=getchar();
    if(f)x=-x;
}
struct SEG{
    int ls,rs,w;
}seg[N*40];
int n,m,ans[N],tofa[N];
int sz[N],dfn[N],tim;
vectormd[N],mp[N];;
int rt[N],snum;

void dfs1(int pos,int fa)
{
    int v;
    sz[pos]=1,dfn[pos]=++tim;
    for(int i=0;i>1;
    if(to<=mid)seg[nw].ls=build(l,mid,to,dt);
    else seg[nw].rs=build(mid+1,r,to,dt);
    push_up(nw);
    return nw;
}
void ins(int nw,int l,int r,int to,int dt)
{
    if(l==r)
    {
        seg[nw].w=min(seg[nw].w,dt);
        return;
    }
    int mid=(l+r)>>1;
    if(to<=mid)
    {
        if(!seg[nw].ls)seg[nw].ls=new_node();
        ins(seg[nw].ls,l,mid,to,dt);
    }
    else
    {
        if(!seg[nw].rs)seg[nw].rs=new_node();
        ins(seg[nw].rs,mid+1,r,to,dt);
    }
    push_up(nw);
}
int mg(int x,int y)
{
    if(!x||!y)return x+y;
    seg[x].w=min(seg[x].w,seg[y].w);
    if(seg[x].ls||seg[y].ls)seg[x].ls=mg(seg[x].ls,seg[y].ls);
    if(seg[x].rs||seg[y].rs)seg[x].rs=mg(seg[x].rs,seg[y].rs);
    return x;
}
int ask(int nw,int L,int R,int l,int r)
{
    if(l>R||r>1,res=2e9;
    if(seg[nw].ls)res=min(res,ask(seg[nw].ls,L,R,l,mid));
    if(seg[nw].rs)res=min(res,ask(seg[nw].rs,L,R,mid+1,r));
    return res;
}
void dfs2(int pos,int fa)
{
    pii v;int nw;
    for(int i=0;i1)ans[nw]=min(ans[nw],ask(rt[pos],1,dfn[pos]-1,1,tim));
        if(dfn[pos]+sz[pos]<=tim)ans[nw]=min(ans[nw],ask(rt[pos],dfn[pos]+sz[pos],tim,1,tim));
    }
}
int main()
{
    int u,v,w;
//	freopen("worry.in","r",stdin);
//	freopen("worry.out","w",stdout);
    read(n),read(m);
    for(int i=1;i1e9)?-1:ans[i]);
}

 

你可能感兴趣的:(刷题集,笔记)