Qtree1,2,3暂缺
Qtree4思路
总是有机房大佬问我怎么做(抄题解不就好了吗?),那我就讲一讲我紊乱的思路吧。
应该不难想到边权下放到子节点,有了这个基础,我们对细节的处理就不用那么模糊了。
先上定义:
为当前辅助树以中序遍历形成的链(在原树上也是一条链),严格以链的开头为起点,往深度递增方向拓展,找到白节点则返回值,找不到返回负无穷。
为当前链的结尾,往深度递减方向拓展。
为当前这层的最优解。
由于 是二叉的,但是原树是多叉的,那么当前状态会有多个儿子,但只有一个是实儿子(即在 中的右儿子),那么我们要多开一个平衡树,来维护虚儿子对答案有贡献的状态。
这时
开一个
,(秒天秒地秒空气),强行维护一下虚儿子的
。返回一个虚儿子最大的
,称之为
,返回一个虚儿子次大的
,称之为
.(以后有用,如果有问题去问致远星),返回虚儿子最大的
,称之为
。
代表白色。
不严谨的状态转移
这难道不是一个DP吗???!(设 为当前状态, 即为p在原链上的祖宗(至少是父亲),辅助树的左儿子, )
但是,有可能没有白节点,这时就需要返回负无穷。
为了方便计算, 可以表示一个实值,也可以表示为颜色;
当 时,为白色,否则, 为负无穷
令
辣么,
状态很多,细细理解。
另外在 时,要改变 ,这时要注意改变实虚儿子。
贴上一份代码
#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,貌似是一道弱化版的题目,连 都不用,辣么,
, 不变。
仔细观察 定义,一定从链的结尾出发,找最近的白点距离。
忽然顿悟(看题解!),先打通x到根的路径( ),再把x旋上辅助树的根节点,辣么,现在 只有左儿子咯(因为x是深度最大的点啊),恰好,就可以发现 就是从 出发,去找最近的白点。
之后就是维护一下状态的事情了。
定义
则有
状态定义参照
照例贴上一份代码
#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思路
貌似这道题不用开 ,真的是太好了!
先仔细想想维护吧,因为要求编号为 的点相连通的点的个数(定义两个点 , 连通为 到 的路径上所有点的颜色相同,包括 和 )
貌似不怎么好搞,如果暴力删边连边,菊花图卡爆了。
开两颗 ,分别代表黑色和白色,应该可行。
但是如何维护,是个要点。先考虑一下如何连边。
既然要考虑权的唯一性,那么可以考虑点权转边,如果一个点 为白色,那么在白色 上,与 在原树的父亲建一条虚边,若构成连通图的话,就对答案有贡献。
询问答案时,找到所在的原树根节点,由于根节点的父亲不在 中,那么就说明根节点的 不为 的颜色,此时需要将根节点旋上辅助树的根( )上,输出根节点的右儿子的答案,即可。
但是如何维护答案?
由于对于一个点 ,它有多个儿子,但只有一个儿子在 上,要多开一个数组来储存虚儿子的数目 。
贴一个代码
#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
*/