Qtree LCT系列

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/zyszlb2003/article/details/96483099

Qtree1,2,3暂缺

Qtree4思路

总是有机房大佬问我怎么做(抄题解不就好了吗?),那我就讲一讲我紊乱的思路吧。

应该不难想到边权下放到子节点,有了这个基础,我们对细节的处理就不用那么模糊了。

先上定义:

l s ls 为当前辅助树以中序遍历形成的链(在原树上也是一条链),严格以链的开头为起点,往深度递增方向拓展,找到白节点则返回值,找不到返回负无穷。

r s rs 为当前链的结尾,往深度递减方向拓展。

m s ms 为当前这层的最优解。

由于 S p l a y Splay 是二叉的,但是原树是多叉的,那么当前状态会有多个儿子,但只有一个是实儿子(即在 S p l a y Splay 中的右儿子),那么我们要多开一个平衡树,来维护虚儿子对答案有贡献的状态。

这时 S T L STL大法好

开一个 s e t set ,(秒天秒地秒空气),强行维护一下虚儿子的 l s ls 。返回一个虚儿子最大的 l s ls ,称之为 h f l s hfls ,返回一个虚儿子次大的 l s ls ,称之为 h s l s hsls .(以后有用,如果有问题去问致远星),返回虚儿子最大的 m s ms ,称之为 c f m s cfms

w = 0 w=0 代表白色。

不严谨的状态转移

这难道不是一个DP吗???!(设 p p 为当前状态, l c = t [ p ] . s o n [ 0 ] lc=t[p].son[0] 即为p在原链上的祖宗(至少是父亲),辅助树的左儿子, r c = t [ p ] . s o n [ 1 ] rc=t[p].son[1]

t [ p ] . l s = m a x { t [ l c ] . l s , t [ l c ] . s + t [ p ] . d + t [ r c ] . l s , t [ l c ] . s + t [ p ] . d + t [ p ] . h f l s , t [ l c ] . s + t [ p ] . d } t[p].ls=max\begin{Bmatrix}t[lc].ls,t[lc].s+t[p].d+t[rc].ls,t[lc].s+t[p].d+t[p].hfls,t[lc].s+t[p].d\end{Bmatrix}

t [ p ] . r s = m a x { t [ r c ] . r s , t [ r c ] . s + t [ p ] . d + t [ l c ] . r s , t [ r c ] . s + t [ p ] . h f l s } t[p].rs=max\begin{Bmatrix}t[rc].rs,t[rc].s+t[p].d+t[lc].rs,t[rc].s+t[p].hfls\end{Bmatrix}

但是,有可能没有白节点,这时就需要返回负无穷。

为了方便计算, t [ p ] . w t[p].w 可以表示一个实值,也可以表示为颜色;

t [ p ] . w = 0 t[p].w=0 时,为白色,否则, t [ p ] . w t[p].w 为负无穷

w = m a x ( t [ p ] . w , t [ p ] . h f l s ) , L = m a x ( w , t [ l c ] . r s + t [ p ] . d ) , R = m a x ( w , t [ r c ] . l s ) w=max(t[p].w,t[p].hfls),L=max(w,t[lc].rs+t[p].d),R=max(w,t[rc].ls)

辣么,

t [ p ] . l s = m a x { t [ l c ] . l s , t [ l c ] . s + t [ p ] . d + R } t[p].ls=max\begin{Bmatrix}t[lc].ls,t[lc].s+t[p].d+R\end{Bmatrix}

t [ p ] . r s = m a x { t [ r c ] . r s , t [ r c ] . s + L } t[p].rs=max\begin{Bmatrix}t[rc].rs,t[rc].s+L\end{Bmatrix}

t [ p ] . m s = m a x { t [ l c ] . m s , t [ r c ] . m s , t [ l c ] . r s + t [ p ] . d + R , L + t [ r c ] . l s , t [ p ] . h f l s , t [ p ] . h f l s + t [ p ] . h s l s , t [ p ] . c f m s , i f ( t [ p ] . w = = 0 ) 0 , t [ p ] . h f l s } t[p].ms=max\begin{Bmatrix}t[lc].ms,t[rc].ms,t[lc].rs+t[p].d+R,L+t[rc].ls,t[p].hfls,\\t[p].hfls+t[p].hsls,t[p].cfms,if(t[p].w==0)0,t[p].hfls\end{Bmatrix}

状态很多,细细理解。

另外在 a c c e s s access 时,要改变 t [ x ] . s o n [ 1 ] t[x].son[1] ,这时要注意改变实虚儿子。

贴上一份代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define gc getchar()
using namespace std;
const int N=1e5+10;
const int inf=1<<28;
inline void qr(int &x)
{
    x=0;char c=gc;int f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
    while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
    x*=f;
}
void qw(int x)
{
    if(x/10)qw(x/10);
    putchar(x%10+48);
}
multiset<int>h[N],c[N];
int fir(multiset<int>&s){return s.size()?*s.rbegin():-inf;}
int sec(multiset<int>&s){return s.size()>1?*(++s.rbegin()):-inf;}
struct node{int ls,ms,rs,hfls,hsls,cfms,s,d,son[2],f,w;}t[N];int ans;
inline bool nroot(int p){return t[t[p].f].son[0]==p||t[t[p].f].son[1]==p;}
void update_set(int p){t[p].hfls=fir(h[p]),t[p].hsls=sec(h[p]),t[p].cfms=fir(c[p]);}
inline void update(int x)
{
    int lc=t[x].son[0],rc=t[x].son[1];
    t[x].s=t[lc].s+t[rc].s+t[x].d;
    int w=max(t[x].w,t[x].hfls);/*如果变白了,t[x].w=0,否则为-inf,这样方便计算*/
    int L=max(w,t[lc].rs+t[x].d);/*L为x的左子树(实际为x的一系列祖宗)。自己理解就好了*/
    int R=max(w,t[rc].ls);/*类似于L,不含x点权*/
    t[x].ls=max(t[lc].ls,t[lc].s+t[x].d+R);/*如果要包含左子树,那么一定要包括x*/
    t[x].rs=max(t[rc].rs,t[rc].s+L);
    /*ms需要继承的状态很多,有当前链的最长距离,左子树右子树的ms,之前统计过的ls的最大值与次大值之和,也就是hfls+hsls*/
    /*之前的最大ms,也就是pfms,如果当前点x为白色,那么也可以考虑hfls,再不济也为0*/
    t[x].ms=max(t[lc].rs+t[x].d+R,t[rc].ls+L);
    t[x].ms=max(max(t[lc].ms,t[rc].ms),t[x].ms);
    t[x].ms=max(t[x].ms,t[x].hfls+t[x].hsls);
    t[x].ms=max(t[x].ms,t[x].cfms);
    if(!t[x].w)t[x].ms=max(max(t[x].ms,t[x].hfls),0);
}
void rotate(int p,int w)
{
    int f=t[p].f,gf=t[f].f;
    int r=t[p].son[w],R=f;t[R].son[w^1]=r;if(r)t[r].f=R;
    r=p;R=gf;if(nroot(f))t[R].son[t[R].son[1]==f]=r;t[r].f=R;
    r=f;R=p;t[R].son[w]=r;t[r].f=R;update(f);update(p);
}
void splay(int p)
{
    while(nroot(p))
    {
        int f=t[p].f,gf=t[f].f;
        if(!nroot(f))rotate(p,t[f].son[0]==p);
        else
        {
            if(t[f].son[0]==p&&t[gf].son[0]==f)rotate(f,1),rotate(p,1);
            else if(t[f].son[0]==p&&t[gf].son[1]==f)rotate(p,1),rotate(p,0);
            else if(t[f].son[1]==p&&t[gf].son[0]==f)rotate(p,0),rotate(p,1);
            else rotate(f,0),rotate(p,0);
        }
    }
}
void access(int x)
{
    for(int y=0;x;x=t[y=x].f)
    {
        splay(x);if(t[x].son[1])h[x].insert(t[t[x].son[1]].ls),c[x].insert(t[t[x].son[1]].ms);
        if(y)h[x].erase(h[x].find(t[y].ls)),c[x].erase(c[x].find(t[y].ms));
        update_set(x);t[x].son[1]=y;update(x);
    }
}
void change(int x)
{
    access(x);splay(x);
    t[x].w?t[x].w=0:t[x].w=-inf;
    update(x);ans=t[x].ms;
}
struct edge{int x,y,d,next;}a[N<<1];int last[N],len;
inline void ins(int x,int y,int d){a[++len]=(edge){x,y,d,last[x]};last[x]=len;}
void maketree(int x)
{
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==t[x].f)continue;
        t[y].f=x;t[y].d=a[k].d;maketree(y);
        h[x].insert(t[y].ls);c[x].insert(t[y].ms);
    }
    update_set(x);update(x);
}
int main()
{
    int n;qr(n);
    for(int i=0;i<=n;i++)t[i].ls=t[i].rs=t[i].ms=-inf;
    for(int i=1;i<n;i++){int x,y,d;qr(x),qr(y),qr(d);ins(x,y,d);ins(y,x,d);}
    maketree(1);ans=t[1].ms;
    int T;qr(T);
    while(T--)
    {
        char s[10];int x;scanf("%s",s+1);
        if(s[1]=='A')ans<0?puts("They have disappeared."):printf("%d\n",ans);
        else qr(x),change(x);
    }
    return 0;
}

Qtree5思路

emmm,貌似是一道弱化版的题目,连 m s ms 都不用,辣么,

l s ls r s rs 不变。

仔细观察 r s rs 定义,一定从链的结尾出发,找最近的白点距离。

忽然顿悟(看题解!),先打通x到根的路径( a c c e s s s ( x ) accesss(x) ),再把x旋上辅助树的根节点,辣么,现在 x x 只有左儿子咯(因为x是深度最大的点啊),恰好,就可以发现 t [ x ] . r s t[x].rs 就是从 x x 出发,去找最近的白点。

之后就是维护一下状态的事情了。

定义 w = m i n ( t [ p ] . w , t [ p ] . h f l s ) , L = m i n ( w , t [ l c ] . r s + t [ p ] . d ) , R = m i n ( w . , t [ r c ] . l s ) ; w=min(t[p].w,t[p].hfls),L=min(w,t[lc].rs+t[p].d),R=min(w.,t[rc].ls);

则有 t p ] . l s = m i n { t [ l c ] . l s , t [ l c ] . s + t [ p ] . d + R } tp].ls=min\begin{Bmatrix}t[lc].ls,t[lc].s+t[p].d+R\end{Bmatrix}

t [ p ] . r s = m i n { t [ r c ] . r s , t [ r c ] . s + L } t[p].rs=min\begin{Bmatrix}t[rc].rs,t[rc].s+L\end{Bmatrix}

状态定义参照 Q t r e e 4 Qtree4

照例贴上一份代码

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<set>
#define gc getchar()
using namespace std;
const int N=1e5+10;
const int inf=1<<28;
inline void qr(int &x)
{
    x=0;char c=gc;int f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
    while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
    x*=f;
}
void qw(int x)
{
    if(x/10)qw(x/10);
    putchar(x%10+48);
}
multiset<int>h[N];
int fir(multiset<int>&s){return s.size()?*s.begin():inf;}
struct node{int f,son[2],s,ls,rs,hfls,w;}t[N];//hfls是x的所有虚儿子的ls的最小值 
inline bool nroot(int p){return t[t[p].f].son[0]==p||t[t[p].f].son[1]==p;}
inline void update_set(int p){t[p].hfls=fir(h[p]);}
inline void update(int p)
{
    int lc=t[p].son[0],rc=t[p].son[1];
    t[p].s=1+t[lc].s+t[rc].s;
    int w=min(t[p].w,t[p].hfls);
    int L=min(w,t[lc].rs+1);
    int R=min(w,t[rc].ls);
    t[p].ls=min(t[lc].ls,t[lc].s+R+1);//t[p].ls一定含链最上面那个点 
    t[p].rs=min(t[rc].rs,t[rc].s+L);//t[p].rs一定含链最下面那个点 
}
void rotate(int p,int w)
{
    int f=t[p].f,gf=t[f].f;
    int r=t[p].son[w],R=f;t[R].son[w^1]=r;if(r)t[r].f=R;
    r=p;R=gf;if(nroot(f))t[R].son[t[R].son[1]==f]=r;t[r].f=R;
    r=f;R=p;t[R].son[w]=r;t[r].f=R;update(f),update(p);
}
void splay(int p)
{
    while(nroot(p))
    {
        int f=t[p].f,gf=t[f].f;
        if(!nroot(f))rotate(p,t[f].son[0]==p);
        else
        {
            if(t[f].son[0]==p&&t[gf].son[0]==f)rotate(f,1),rotate(p,1);
            else if(t[f].son[0]==p&&t[gf].son[1]==f)rotate(p,1),rotate(p,0);
            else if(t[f].son[1]==p&&t[gf].son[0]==f)rotate(p,0),rotate(p,1);
            else rotate(f,0),rotate(p,0);
        }
    }
}
void access(int x)
{
    for(int y=0;x;x=t[y=x].f)
    {
        splay(x);if(t[x].son[1])h[x].insert(t[t[x].son[1]].ls);
        if(y)h[x].erase(h[x].find(t[y].ls));
        t[x].son[1]=y;
        update_set(x);update(x);
    }
}
int n;
void query(int x)
{
    access(x);splay(x);
    if(t[x].rs>n)puts("-1");
    else qw(t[x].rs),puts("");
}
void change(int x)
{
    access(x);splay(x);
    t[x].w?t[x].w=0:t[x].w=inf;
    update(x);
}
struct edge{int x,y,next;}a[N<<1];int len,last[N];
void ins(int x,int y){a[++len]=(edge){x,y,last[x]};last[x]=len;}
void maketree(int x)
{
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==t[x].f)continue;
        t[y].f=x;maketree(y);
        h[x].insert(t[y].ls);
    }
    update_set(x);update(x);
}
int main()
{
    qr(n);
    for(int i=0;i<=n;i++)t[i].w=t[i].ls=t[i].rs=inf;
    for(int i=1;i<=n-1;i++){int x,y;qr(x);qr(y);ins(x,y);ins(y,x);}
    maketree(1);
    int m;qr(m);
    while(m--)
    {
        int opt,x;qr(opt),qr(x);
        if(!opt)change(x);
        else query(x);
    }
    return 0;
}

Qtree6思路

貌似这道题不用开 m u l t i s e t multiset ,真的是太好了!

先仔细想想维护吧,因为要求编号为 x x 的点相连通的点的个数(定义两个点 x x y y 连通为 x x t t 的路径上所有点的颜色相同,包括 x x y y )

貌似不怎么好搞,如果暴力删边连边,菊花图卡爆了。

开两颗 L C T LCT ,分别代表黑色和白色,应该可行。

但是如何维护,是个要点。先考虑一下如何连边。

既然要考虑权的唯一性,那么可以考虑点权转边,如果一个点 p p 为白色,那么在白色 L C T LCT 上,与 p p 在原树的父亲建一条虚边,若构成连通图的话,就对答案有贡献。

询问答案时,找到所在的原树根节点,由于根节点的父亲不在 L C T LCT 中,那么就说明根节点的 c o l col 不为 L C T LCT 的颜色,此时需要将根节点旋上辅助树的根( s p l a y ( r t ) splay(rt) )上,输出根节点的右儿子的答案,即可。

但是如何维护答案?

由于对于一个点 x x ,它有多个儿子,但只有一个儿子在 S p l a y Splay 上,要多开一个数组来储存虚儿子的数目 c c

贴一个代码

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#define gc getchar()
using namespace std;
const int N=1e5+10;
inline void qr(int &x)
{
    x=0;int f=1;char c=gc;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
    while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
    x*=f;
}
void qw(int x)
{
    if(x/10)qw(x/10);
    putchar(x%10+48);
}
int fa[N],col[N];
struct LCT
{
    struct node{int f,son[2],c,s;}t[N];
    LCT(){for(int i=1;i<N;i++)t[i].s=1;}
    inline bool nroot(int p){return t[t[p].f].son[0]==p||t[t[p].f].son[1]==p;}
    inline void update(int p){t[p].s=t[t[p].son[0]].s+t[t[p].son[1]].s+t[p].c+1;}//c为虚 
    void rotate(int p,int w)
    {
        int f=t[p].f,gf=t[f].f;
        int r=t[p].son[w],R=f;t[R].son[w^1]=r;if(r)t[r].f=R;
        r=p;R=gf;if(nroot(f))t[R].son[t[R].son[1]==f]=r;t[r].f=R;
        r=f;R=p;t[R].son[w]=r;t[r].f=R;update(f),update(p);
    }
    void splay(int p)
    {
        while(nroot(p))
        {
            int f=t[p].f,gf=t[f].f;
            if(!nroot(f))rotate(p,t[f].son[0]==p);
            else
            {
                if(t[f].son[0]==p&&t[gf].son[0]==f)rotate(f,1),rotate(p,1);
                else if(t[f].son[0]==p&&t[gf].son[1]==f)rotate(p,1),rotate(p,0);
                else if(t[f].son[1]==p&&t[gf].son[0]==f)rotate(p,0),rotate(p,1);
                else rotate(f,0),rotate(p,0);
            }
        }
    }
    void access(int x)
    {
        for(int y=0;x;x=t[y=x].f)
            splay(x),t[x].c+=t[t[x].son[1]].s,t[x].c-=t[t[x].son[1]=y].s;
    }
    int findroot(int x)
    {
        access(x);splay(x);
        while(t[x].son[0])x=t[x].son[0];
        splay(x);
        return x;   
    }
    void link(int x)
    {
        splay(x);
        int y=t[x].f=fa[x];
        access(y);splay(y);
        t[y].c+=t[x].s,t[y].s+=t[x].s;
    }
    void cut(int x)
    {
        access(x);splay(x);
        t[x].son[0]=t[t[x].son[0]].f=0;
        update(x);
    }
}lct[2];
struct edge{int x,y,next;}a[N<<1];int len,last[N];
inline void ins(int x,int y){a[++len]=(edge){x,y,last[x]};last[x]=len;}
void dfs(int x)
{
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;if(y==fa[x])continue;
        fa[y]=x;
        dfs(y);
        lct[0].link(y);
    }
}
int main()
{
    len=0;
    int n;qr(n);
    for(int i=1;i<n;i++)
    {
        int x,y;qr(x),qr(y);
        ins(x,y);ins(y,x);
    }
    dfs(1);fa[1]=n+1;
    lct[0].link(1);
    int m;qr(m);
    while(m--)
    {
        int opt,x;qr(opt),qr(x);
        if(opt)lct[col[x]].cut(x),lct[col[x]=!col[x]].link(x);
        else
        {
            int rt=lct[col[x]].findroot(x);
            qw(lct[col[x]].t[lct[col[x]].t[rt].son[1]].s);puts("");
        }
    }
    return 0;
}

Qtree7 思路

和Qtree6差不多吧。

不过开多一个multiset来存虚儿子的信息即可。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<set>
#define gc getchar()
using namespace std;
const int N=1e5+10;
const int inf=2147483647;
inline void qr(int &x)
{
	x=0;int f=1;char c=gc;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
	x*=f;
}
void qw(int x)
{
	if(x<0)x=-x,putchar('-');
	if(x/10)qw(x/10);
	putchar(x%10+48);
}
int fa[N],col[N],d[N];
struct LCT
{
	multiset<int>h[N];
	int fir(multiset<int>&s){return s.size()?*s.rbegin():-inf;}
	struct node{int f,son[2],ms,hfms;}t[N];
	LCT(){for(int i=0;i<N;i++)t[i].ms=t[i].hfms=-inf;}
	inline bool nroot(int p){return t[t[p].f].son[0]==p||t[t[p].f].son[1]==p;}
	inline void update_set(int p){t[p].hfms=fir(h[p]);}
	inline void update(int p)
	{
		int lc=t[p].son[0],rc=t[p].son[1];
		t[p].ms=max(t[lc].ms,max(t[rc].ms,max(t[p].hfms,d[p])));
	}
	void rotate(int p,int w)
	{
		int f=t[p].f,gf=t[f].f;
		int r=t[p].son[w],R=f;t[R].son[w^1]=r;if(r)t[r].f=R;
		r=p;R=gf;if(nroot(f))t[R].son[t[R].son[1]==f]=r;t[r].f=R;
		r=f;R=p;t[R].son[w]=r;t[r].f=R;update(f),update(p);
	}
	void splay(int p)
	{
		while(nroot(p))
		{
			int f=t[p].f,gf=t[f].f;
			if(!nroot(f))rotate(p,t[f].son[0]==p);
			else
			{
				if(t[f].son[0]==p&&t[gf].son[0]==f)rotate(f,1),rotate(p,1);
				else if(t[f].son[0]==p&&t[gf].son[1]==f)rotate(p,1),rotate(p,0);
				else if(t[f].son[1]==p&&t[gf].son[0]==f)rotate(p,0),rotate(p,1);
				else rotate(f,0),rotate(p,0);
			}
		}
	}
	void access(int x)
	{
		for(int y=0;x;x=t[y=x].f)
		{
			splay(x);if(t[x].son[1])h[x].insert(t[t[x].son[1]].ms);
			if(y)h[x].erase(h[x].find(t[y].ms));
			t[x].son[1]=y;update_set(x);update(x);
		}
	}
	int findroot(int x)
	{
		access(x);splay(x);
		while(t[x].son[0])x=t[x].son[0];
		splay(x);
		return x;	
	}
	void link(int x)
	{
		splay(x);
		int y=t[x].f=fa[x];
		access(y);splay(y);
		t[y].son[1]=x;update(x);update(y);
	}
	void cut(int x)
	{
		access(x);splay(x);
		t[x].son[0]=t[t[x].son[0]].f=0;
		update(x);
	}
	void change(int x)
	{
		access(x);splay(x);
		qr(d[x]);update(x);
	}
}lct[2];
struct edge{int x,y,next;}a[N<<1];int len,last[N];
inline void ins(int x,int y){a[++len]=(edge){x,y,last[x]};last[x]=len;}
void dfs(int x)
{
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;if(y==fa[x])continue;
		fa[y]=x;dfs(y);
	}
	lct[col[x]].link(x);
}
int main()
{
	len=0;
	int n;qr(n);
	for(int i=1;i<n;i++)
	{
		int x,y;qr(x),qr(y);
		ins(x,y);ins(y,x);
	}
	for(int i=1;i<=n;i++)qr(col[i]);
	for(int i=1;i<=n;i++)qr(d[i]);
	fa[1]=n+1;
	dfs(1);
	int m;qr(m);
	while(m--)
	{
		int opt,x;qr(opt),qr(x);
		if(opt==1)lct[col[x]].cut(x),lct[col[x]=!col[x]].link(x);
		else if(opt==0)
		{
			int rt=lct[col[x]].findroot(x);
			qw(lct[col[x]].t[lct[col[x]].t[rt].son[1]].ms);puts("");
		}
		else if(opt==2)
			lct[col[x]].change(x);
	}
	return 0;
}
/*
7
1 2
1 3
2 4
2 5
3 6
3 7
0 0 0 0 0 0 0
-1 -2 -3 -4 -5 -6 -7
4
0 1
1 1
0 2
0 3
*/

猜你喜欢

转载自blog.csdn.net/zyszlb2003/article/details/96483099