最后的树链刨分

  学了这么久树刨了 A了这么多树刨的题了。也是时候改总价一下了。

树链刨分 之重链刨分 划分轻重链 使其有较快且稳定的求LCA的复杂度绝对之logn

再把这些链放到 一些高级数据结构中 然后就可以快速求的链上的信息或修改链上信息。

关于求LCA 我一直都没有证明过为什么树刨可找LCA 其实非常显然。

证明:对于树上一点其祖先是其 到根的路径上的点,两点的最近公共祖先显然是两点不断向上爬爬到一个两点都可以到达的最近的地方。

已有两点 如果他们这条链上的最顶端的点不同那说明不在同一条链上,考虑让他们向上爬显然我们是不知道要爬到什么地方的,

那此时 我们判断他们的这条链最顶端的深度 深度越低的肯定是暂时不需要爬 深度低的一定要向上爬因为那个点是不可能爬到这条链上的且那条立案的顶端比这条链的顶端低也就是说LCA是不可能在这条链上的所以考虑让这个点向上爬爬到那条链的最末。这样不断的爬最终求的LCA 复杂度logn 比倍增常数要小上很多吧。而且还快。

总结完了放最后的例题也就是树刨和其他算法的结合。

Qtree1 我写过了 前面博客中是有的,这道题是第二道水题。第一问求a b之间的路径之和 这个简单啊,而且发现不带修改 所以直接dfs一遍跑LCA 然后O(1) 输出答案即可。第二问求第k个点的编号 这个也简单啊 倍增?好像是可以的 我们可以控制一下深度什么的乱写应该是没有什么问题的。

但是更简单的却是树刨两点同时向上跳时累加答案即可。不必要的是第一问我还写了一棵线段树我以为带修改不过没关系跑的很快。

//#incldue<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define l(x) t[x].l
#define r(x) t[x].r
#define sum(x) t[x].sum
#define INF 2147483646
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
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=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    x<0?putchar('-'),x=-x:0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('
');return;
}
const int MAXN=100002;
int n,cnt,len,T;
int d[MAXN],son[MAXN],w[MAXN],b[MAXN];
int f[MAXN],sz[MAXN],top[MAXN],id[MAXN];
int lin[MAXN<<1],nex[MAXN<<1],ver[MAXN<<1],e[MAXN<<1];
int q[100],q1[100],h;
char a[10];
struct wy
{
    int l,r;
    int sum;
}t[MAXN<<2];
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline void build(int p,int l,int r)
{
    l(p)=l;r(p)=r;
    if(l==r){sum(p)=w[b[l]];return;}
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    sum(p)=sum(p<<1)+sum(p<<1|1);
    return;
}
inline void dfs(int x,int father)
{
    sz[x]=1;f[x]=father;
    d[x]=d[father]+1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        dfs(tn,x);
        w[tn]=e[i];
        sz[x]=sz[x]+sz[tn];
        if(sz[son[x]]<sz[tn])son[x]=tn;
    }
}
inline void dp(int x,int father)
{
    top[x]=father;
    id[x]=++cnt;
    b[cnt]=x;
    if(!son[x])return;
    dp(son[x],father);
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn!=f[x]&&tn!=son[x])
        dp(tn,tn);
    }
    return;
}
inline int ask(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p))return sum(p);
    int cnt=0;
    int mid=(l(p)+r(p))>>1;
    if(l<=mid)cnt+=ask(p<<1,l,r);
    if(r>mid)cnt+=ask(p<<1|1,l,r);
    return cnt;
}
inline int Task(int x,int y)
{
    int fx=top[x],fy=top[y];
    int ans=0;
    while(fx!=fy)
    {
        if(d[fx]<d[fy])swap(x,y),swap(fx,fy);
        ans+=ask(1,id[fx],id[x]);
        x=f[fx];fx=top[x];
    }
    if(id[x]<id[y])swap(x,y);
    if(id[y]+1<=id[x])ans+=ask(1,id[y]+1,id[x]);
    return ans;
}
inline int KTHask(int x,int y,int z)
{
    h=0;int ans=0,s;
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(d[fx]>d[fy])
        {
            s=id[x]-id[fx]+1;
            if(s<z)z-=s;
            else ans=id[x]-z+1;
            x=f[fx];fx=top[x];
        }
        else
        {
            q[++h]=id[fy];q1[h]=id[y];
            y=f[fy];fy=top[y];
        }
        if(ans)return b[ans];
    }
    if(d[x]<d[y])q[++h]=id[x],q1[h]=id[y];
    else
    {
        s=id[x]-id[y]+1;
        if(s<z)z-=s;
        else ans=id[x]-z+1;
    }
    if(ans)return b[ans];
    for(int i=h;i>=1;--i)
    {
        s=q1[i]-q[i]+1;
        if(s<z)z-=s;
        else ans=q[i]+z-1;
        if(ans)return b[ans];
    }
    return b[ans];
}
int main()
{
    //freopen("1.in","r",stdin);
    T=read();
    while(T--)
    {
        len=0;cnt=0;
        memset(lin,0,sizeof(lin));
        memset(son,0,sizeof(son));
        n=read();
        for(int i=1;i<n;++i)
        {
            int x,y,z;
            x=read();y=read();z=read();
            add(x,y,z);add(y,x,z);
        }
        dfs(1,0);dp(1,1);
        build(1,1,cnt);
        while(1)
        {
            int x,y,z;
            scanf("%s",a+1);
            if(a[1]=='D'&&a[2]=='O')break;
            x=read();y=read();
            if(a[1]=='D'){put(Task(x,y));continue;}
            z=read();
            put(KTHask(x,y,z));
        }
    }
}
View Code

这道题也是一道水题 求路径上的第一个黑点 。又没认真看题目导致我wa了两次。

求的是1到x的距离注意取min即可。

//#incldue<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define l(x) t[x].l
#define r(x) t[x].r
#define sum(x) t[x].sum
#define INF 2147483646
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
inline void put(int x)
{
    x<0?putchar('-'),x=-x:0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('
');return;
}
const int MAXN=100002;
int n,m,cnt,len;
int d[MAXN],son[MAXN],w[MAXN],b[MAXN];
int f[MAXN],sz[MAXN],top[MAXN],id[MAXN];
int lin[MAXN<<1],nex[MAXN<<1],ver[MAXN<<1];
int q[MAXN],q1[MAXN],h;
struct wy
{
    int l,r;
    int sum;
}t[MAXN<<2];
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x>y?y:x;}
inline void add(int x,int y)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
}
inline void build(int p,int l,int r)
{
    l(p)=l;r(p)=r;
    if(l==r){sum(p)=INF;return;}
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    sum(p)=min(sum(p<<1),sum(p<<1|1));
    return;
}
inline void dfs(int x,int father)
{
    sz[x]=1;f[x]=father;
    d[x]=d[father]+1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        dfs(tn,x);
        sz[x]=sz[x]+sz[tn];
        if(sz[son[x]]<sz[tn])son[x]=tn;
    }
}
inline void dp(int x,int father)
{
    top[x]=father;
    id[x]=++cnt;
    b[cnt]=x;
    if(!son[x])return;
    dp(son[x],father);
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn!=f[x]&&tn!=son[x])
        dp(tn,tn);
    }
    return;
}
inline void change(int p,int x)
{
    if(l(p)==r(p)){w[l(p)]^=1;if(w[l(p)])sum(p)=l(p);else sum(p)=INF;return;}
    int mid=(l(p)+r(p))>>1;
    if(x<=mid)change(p<<1,x);
    else change(p<<1|1,x);
    sum(p)=min(sum(p<<1),sum(p<<1|1));
    return;
}
inline int ask(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p))return sum(p);
    int mid=(l(p)+r(p))>>1;
    int cnt=INF;
    if(l<=mid)cnt=min(cnt,ask(p<<1,l,r));
    if(r>mid)cnt=min(cnt,ask(p<<1|1,l,r));
    return cnt;
}
inline int Task(int x)
{
    int fx=top[x],ans=-1;h=0;
    while(fx!=1)
    {
        q[++h]=id[fx];q1[h]=id[x];
        x=f[fx];fx=top[x];
    }
    ans=ask(1,id[1],id[x]);
    if(ans!=INF)return b[ans];
    for(int i=h;i>=1;--i)
    {
        ans=ask(1,q[i],q1[i]);
        if(ans!=INF)return b[ans];
    }
    return -1;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<n;++i)
    {
        int x,y;
        x=read();y=read();
        add(x,y);add(y,x);
    }
    dfs(1,0);dp(1,1);
    build(1,1,n);
    for(int i=1;i<=m;++i)
    {
        int p,x;
        p=read();x=read();
        if(p==0)change(1,id[x]);
        else put(Task(x));
    }
    return 0;
}
View Code

这道题 是求出树种两点的最远白点的距离 这个修改的话O(1) 修改 第二问不太好求。

莫非是点分治 好像答案是这样统计的 点分治一次是 nlogn的 而求一次树的直径是O(n)貌似这也不是纯树的直径 要是我的话应该选择点分治这个一定是对的。但是带修改的话 就麻烦了。这复杂度很高 近乎nmlog 考虑树剖 我发现这其实答案在以某点为子树中。

hh 被尧神吵了 的确 不学数据结构了 这道题 好像是什么 链分治 什么 动态点分治 什么 树刨神奇操作。

我不会 退坑了。 数据结构88.下次再见我会把splay LCT 都学会的!

这道题 是一个基础题我却一直wa 第一次 貌似ask的时候没有pushup

第二次 好像数组开小了 第三次 这个懒标记我没有异或!!! 第4次那个懒标记我没有异或!!!!!

//#incldue<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define l(x) t[x].l
#define r(x) t[x].r
#define sum(x) t[x].sum
#define mi(x) t[x].mi
#define mx(x) t[x].mx
#define tag(x) t[x].tag
#define INF 2147483646
#define max(x,y) (x>y?x:y)
#define min(x,y) (x>y?y:x)
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
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=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    x<0?putchar('-'),x=-x:0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar('
');return;
}
const int MAXN=100002;
int n,m,cnt,len;
int minn,maxx,sigam;
int lin[MAXN],nex[MAXN<<1],ver[MAXN<<1],e[MAXN<<1];
int f[MAXN],sz[MAXN],top[MAXN],id[MAXN];
int d[MAXN],son[MAXN],w[MAXN],pos[MAXN],b[MAXN];
char a[5];
struct wy
{
    int l,r;
    int sum;
    int mi,mx;
    int tag;
}t[MAXN<<2];
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline void dfs(int x,int father)
{
    sz[x]=1;f[x]=father;
    d[x]=d[father]+1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        dfs(tn,x);w[tn]=e[i];b[(i+1)>>1]=tn;
        sz[x]=sz[x]+sz[tn];
        if(sz[son[x]]<sz[tn])son[x]=tn;
    }
}
inline void dp(int x,int father)
{
    top[x]=father;
    id[x]=++cnt;pos[cnt]=x;
    if(!son[x])return;
    dp(son[x],father);
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn!=f[x]&&tn!=son[x])
        dp(tn,tn);
    }
    return;
}
inline void build(int p,int l,int r)
{
    l(p)=l;r(p)=r;
    if(l==r){sum(p)=mi(p)=mx(p)=w[pos[l]];return;}
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    sum(p)=sum(p<<1)+sum(p<<1|1);
    mi(p)=min(mi(p<<1),mi(p<<1|1));
    mx(p)=max(mx(p<<1),mx(p<<1|1));
    return;
}
inline void pushdown(int p)
{
    sum(p<<1)=-sum(p<<1);
    sum(p<<1|1)=-sum(p<<1|1);
    int v=mi(p<<1);mi(p<<1)=-mx(p<<1);mx(p<<1)=-v;
    v=mi(p<<1|1);mi(p<<1|1)=-mx(p<<1|1);mx(p<<1|1)=-v;
    tag(p<<1)^=1;tag(p<<1|1)^=1;tag(p)=0;
    return;
}
inline void ask(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p))
    {
        sigam+=sum(p);
        maxx=max(maxx,mx(p));
        minn=min(minn,mi(p));
        return;
    }
    if(tag(p))pushdown(p);
    int mid=(l(p)+r(p))>>1;
    if(l<=mid)ask(p<<1,l,r);
    if(r>mid)ask(p<<1|1,l,r);
    sum(p)=sum(p<<1)+sum(p<<1|1);
    mi(p)=min(mi(p<<1),mi(p<<1|1));
    mx(p)=max(mx(p<<1),mx(p<<1|1));
    return;
}
inline void Task(int x,int y)
{
    int fx=top[x],fy=top[y];
    minn=INF,maxx=-INF,sigam=0;
    while(fx!=fy)
    {
        if(d[fx]<d[fy])swap(x,y),swap(fx,fy);
        ask(1,id[fx],id[x]);
        x=f[fx];fx=top[x];
    }
    if(d[x]<d[y])swap(x,y);
    if(id[y]+1<=id[x])ask(1,id[y]+1,id[x]);
    return;
}
inline void change(int p,int x,int k)
{
    if(l(p)==r(p))
    {
        mi(p)=mx(p)=sum(p)=k;
        return;
    }
    int mid=(l(p)+r(p))>>1;
    if(tag(p))pushdown(p);
    if(x<=mid)change(p<<1,x,k);
    else change(p<<1|1,x,k);
    sum(p)=sum(p<<1)+sum(p<<1|1);
    mi(p)=min(mi(p<<1),mi(p<<1|1));
    mx(p)=max(mx(p<<1),mx(p<<1|1));
    return;
}
inline void change1(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p))
    {
        tag(p)^=1;
        sum(p)=-sum(p);
        int v=mi(p);mi(p)=-mx(p);mx(p)=-v;
        return;
    }
    if(tag(p))pushdown(p);
    int mid=(l(p)+r(p))>>1;
    if(l<=mid)change1(p<<1,l,r);
    if(r>mid)change1(p<<1|1,l,r);
    sum(p)=sum(p<<1)+sum(p<<1|1);
    mi(p)=min(mi(p<<1),mi(p<<1|1));
    mx(p)=max(mx(p<<1),mx(p<<1|1));
}
inline void Tchange(int x,int y)
{
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(d[fx]<d[fy])swap(x,y),swap(fx,fy);
        change1(1,id[fx],id[x]);
        x=f[fx];fx=top[x];
    }
    if(d[x]<d[y])swap(x,y);
    if(id[y]+1<=id[x])change1(1,id[y]+1,id[x]);
    return;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<n;++i)
    {
        int x,y,z;
        x=read()+1;y=read()+1;z=read();
        add(x,y,z);add(y,x,z);
    }
    m=read();
    dfs(1,0);dp(1,1);
    build(1,1,cnt);
    for(int i=1;i<=m;++i)
    {
        scanf("%s",a+1);
        int x,y;
        x=read()+1;y=read()+1;
        if(a[1]=='S'){Task(x,y);put(sigam);}
        if(a[2]=='A'){Task(x,y);put(maxx);}
        if(a[2]=='I'){Task(x,y);put(minn);}
        if(a[1]=='C')change(1,id[b[x-1]],y-1);
        if(a[1]=='N')Tchange(x,y);
    }
    return 0;
}
View Code

本SB已被自己活活气死。

原文地址:https://www.cnblogs.com/chdy/p/10827228.html