QTREE

/*
题目大意:维护一棵树,允许修改边权以及查询链上最大值
题解:我们将边权转为点权,标记在深度较深的点上,树链剖分后用线段树处理即可
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=20010;
int top[N],nxt[N],v[N],g[N],d[N],son[N],f[N],size[N],st[N],en[N],dfn,ed;
void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void init(){
memset(g,dfn=ed=0,sizeof(g));
memset(v,0,sizeof(v));
memset(nxt,0,sizeof(nxt));
memset(son,-1,sizeof(son));
}
void dfs(int x){
size[x]=1;
for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
f[v[i]]=x,d[v[i]]=d[x]+1;
dfs(v[i]),size[x]+=size[v[i]];
if(size[v[i]]>size[son[x]])son[x]=v[i];
}
}
void dfs2(int x,int y){
st[x]=++dfn;top[x]=y;
if(son[x])dfs2(son[x],y);
for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]);
en[x]=dfn;
}
struct Node{int l,r;int Max;}Tree[N*3];
void build(int x,int l,int r){
Tree[x].l=l;Tree[x].r=r;Tree[x].Max=0;
if(l==r)return;int mid=(l+r)>>1;
build(x<<1,l,mid);build((x<<1)|1,mid+1,r);
}
void up(int x){Tree[x].Max=max(Tree[x<<1].Max,Tree[(x<<1)|1].Max);}
void update(int x,int k,int val){
if(Tree[x].l==k&&Tree[x].r==k){Tree[x].Max=val;return;}
int mid=(Tree[x].l+Tree[x].r)>>1;
if(k<=mid)update(x<<1,k,val);
else update((x<<1)|1,k,val);
up(x);
}
int query(int x,int l,int r){
if(Tree[x].l==l&&Tree[x].r==r)return Tree[x].Max;
int mid=(Tree[x].l+Tree[x].r)>>1;
if(r<=mid)return query(x<<1,l,r);
else if(l>mid)return query((x<<1)|1,l,r);
else return max(query(x<<1,l,mid),query((x<<1)|1,mid+1,r));
}
int find(int x,int y){
int tmp=0;
for(;top[x]!=top[y];x=f[top[x]]){
if(d[top[x]]<d[top[y]]){int z=x;x=y;y=z;}
tmp=max(tmp,query(1,st[top[x]],st[x]));
}if(x==y)return tmp;
if(d[x]>d[y]){int z=x;x=y;y=z;}
return max(tmp,query(1,st[son[x]],st[y]));
}int e[N][3];
int main(int n,int T){
scanf("%d",&T);
while(T--){
init(); scanf("%d",&n);
for(int i=0;i<n-1;i++){
scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);
add(e[i][0],e[i][1]);
add(e[i][1],e[i][0]);
}dfs(1);dfs2(1,1);
build(1,1,dfn);
for(int i=0;i<n-1;i++){
if(d[e[i][0]]>d[e[i][1]])swap(e[i][0],e[i][1]);
update(1,st[e[i][1]],e[i][2]);
}char op[10]; int u,w;
while(scanf("%s",op)==1){
if(op[0]=='D')break;
scanf("%d%d",&u,&w);
if(op[0]=='Q')printf("%d\n",find(u,w));
else update(1,st[e[u-1][1]],w);
}
}return 0;
}

QTREE2

/*
题目大意:给出一棵边权树,要求实现树链求和以及求链上第k个元素
题解:边权转点权后用LCT维护,注意求Qkth和sum时区别于点权树的讨论
*/
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int N=300010;
namespace Link_Cut_Tree{
vector<int> v[N],w[N];
int f[N],son[N][2],tmp[N],size[N],val[N],sum[N];
void init(){
memset(val,0,sizeof(val));
memset(sum,0,sizeof(sum));
memset(f,0,sizeof(f));
memset(son,0,sizeof(son));
for(int i=1;i<N;i++)size[i]=1,v[i].clear(),w[i].clear();
}
bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
void up(int x){
sum[x]=val[x]+sum[son[x][0]]+sum[son[x][1]];
size[x]=size[son[x][0]]+size[son[x][1]]+1;
}
void rotate(int x){
int y=f[x],w=son[y][1]==x;
son[y][w]=son[x][w^1];
if(son[x][w^1])f[son[x][w^1]]=y;
if(f[y]){
int z=f[y];
if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x;
}f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
}
void splay(int x){
int s=1,i=x,y;tmp[1]=i;
while(!isroot(i))tmp[++s]=i=f[i];
while(!isroot(x)){
y=f[x];
if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
rotate(x);
}up(x);
}
void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);}
// 求边权树的链和
int ask(int x,int y){
access(y),y=0;
int ans=0;
while(x){
splay(x);
if(!f[x])return sum[son[x][1]]+sum[y];
son[x][1]=y; up(x);
x=f[y=x];
}
}
void dfs(int x,int fx){
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(y==fx)continue;
f[y]=x;
val[y]=w[x][i];
dfs(y,x);
}up(x);
}
int kth(int x,int k){
while(size[son[x][0]]+1!=k){
if(k<size[son[x][0]]+1)x=son[x][0];
else{
k-=size[son[x][0]]+1;
x=son[x][1];
}
}return x;
}
// 求边权树u->v的第k个点
int Qkth(int u,int v,int k){
access(u),u=0;
while(v){
splay(v);
if(!f[v]){
if(size[son[v][1]]+1==k)return v;
else if(size[son[v][1]]+1>k)return kth(son[v][1],size[son[v][1]]+1-k);
else return kth(u,k-(size[son[v][1]]+1));
}son[v][1]=u;
up(v); v=f[u=v];
}
}
}
using namespace Link_Cut_Tree;
int T,x,y,z,k,n;
char op[10];
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
init();
for(int i=1;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
v[x].push_back(y);
w[x].push_back(z);
v[y].push_back(x);
w[y].push_back(z);
}dfs(1,0);
while(scanf("%s",op)){
if(op[1]=='O')break;
if(op[0]=='D'){
scanf("%d%d",&x,&y);
printf("%d\n",ask(x,y));
}
else{
scanf("%d%d%d",&x,&y,&k);
printf("%d\n",Qkth(x,y,k));
}
}
}return 0;
}

QTREE3

/*
题目大意:给出一棵一开始都是白点的树,要求维护操作:
1.将某个点的颜色取反(黑白互换)
2.查询1到x的路径上第一个黑色的点
题解:LCT维护子树中黑点的数量,对于颜色取反,则直接flap,
对于查询操作通过access(x)将1到x这条链取出,splay上查找即可
注意是从1到x,所以是makeroot(1),保证1节点的键值最小
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=100010;
namespace Link_Cut_Tree{
int f[N],son[N][2],val[N],sum[N],tmp[N];bool rev[N];
void init(){
memset(f,0,sizeof(f));
memset(son,0,sizeof(son));
memset(val,0,sizeof(val));
memset(rev,0,sizeof(rev));
memset(sum,0,sizeof(sum));
}
bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
void rev1(int x){if(!x)return;swap(son[x][0],son[x][1]);rev[x]^=1;}
void pb(int x){if(rev[x])rev1(son[x][0]),rev1(son[x][1]),rev[x]=0;}
void up(int x){sum[x]=sum[son[x][0]]+sum[son[x][1]]+val[x];}
void rotate(int x){
int y=f[x],w=son[y][1]==x;
son[y][w]=son[x][w^1];
if(son[x][w^1])f[son[x][w^1]]=y;
if(f[y]){
int z=f[y];
if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x;
}f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
}
void splay(int x){
int s=1,i=x,y;tmp[1]=i;
while(!isroot(i))tmp[++s]=i=f[i];
while(s)pb(tmp[s--]);
while(!isroot(x)){
y=f[x];
if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
rotate(x);
}up(x);
}
void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);}
int root(int x){access(x);splay(x);while(son[x][0])x=son[x][0];return x;}
void makeroot(int x){access(x);splay(x);rev1(x);}
void link(int x,int y){makeroot(x);f[x]=y;access(x);}
int flip(int x){makeroot(x);val[x]^=1;up(x);}
// 查询从1->x的第一个黑点
int ask(int x){
makeroot(1);
access(x); splay(x); // splay只保证了x到根节点标记的下传
if(!sum[x])return -1;
while(x){
pb(x); // 因为有makeroot操作,所以需要下传标记
if(!sum[son[x][0]]&&val[x]==1)return x;
else if(sum[son[x][0]])x=son[x][0];
else x=son[x][1];
}
}
}
using namespace Link_Cut_Tree;
int n,q,x,y;
int main(){
while(~scanf("%d%d",&n,&q)){
init();
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
link(x,y);
}
while(q--){
scanf("%d%d",&x,&y);
if(x)printf("%d\n",ask(y));
else flip(y);
}
}return 0;
}

QTREE4

/*
题目大意:给出一棵边权树,一开始所有点都是白点,
要求维护两种操作:
1.颜色取反(黑白互变)
2.查询树上最远的两个白点之间的距离
题解:我们维护重心树,对于每个重心维护分治层所有白点到其距离,
将其保存在对应方向子重心的优先队列中,
重心处另外维护一个队列保存该分治层每个子重心所产生的最大答案,即最大值加次大值,
那么每个重心处产生的最大答案的最值就是答案,
考虑修改问题,等价于在处理优先队列的删除问题,
对于每个需要删除操作的优先队列,我们额外创建一个优先队列将删除元素加入其中,
当两个队列top元素相同时同时删去即可。
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N=200010;
const int INF=0x3f3f3f3f;
int tot,g[N],nxt[N<<1],v[N<<1],w[N<<1];
int TOT,G[N],NXT[N*20],V[N*20],W[N*20];
void init(){
memset(g,-1,sizeof(g));tot=0;
memset(G,-1,sizeof(G));TOT=0;
}
void add_edge(int x,int y,int z){v[tot]=y,w[tot]=z,nxt[tot]=g[x],g[x]=tot++;}
void ADD_EDGE(int x,int y,int z){V[TOT]=y,W[TOT]=z,NXT[TOT]=G[x],G[x]=TOT++;}
int sum,root,size[N],dp[N],vis[N];
void getroot(int x,int fx){
size[x]=1; dp[x]=0;
for(int i=g[x];~i;i=nxt[i]){
if(!vis[v[i]]&&v[i]!=fx){
getroot(v[i],x);
size[x]+=size[v[i]];
dp[x]=max(dp[x],size[v[i]]);
}
}dp[x]=max(dp[x],sum-size[x]);
if(dp[x]<dp[root])root=x;
}
struct Que{
priority_queue<int> Q,D;
void clear(){
while(!Q.empty())Q.pop();
while(!D.empty())D.pop();
}
void add(int x){Q.push(x);}
void del(int x){D.push(x);}
int top(){
for(;;){
if(Q.empty())return -INF;
else if(!D.empty()&&Q.top()==D.top())Q.pop(),D.pop();
else return Q.top();
}
}
int toptwo(){
int x=top();
del(x);
int y=top();
add(x);
if(y==-INF)return x==-INF?x:0;
return max(x+y,0);
}
}P[N],Q[N],ans;
int dis[N],col[N];
void getdis(int rt,int x,int fx){
Q[rt].add(dis[x]);
for(int i=g[x];~i;i=nxt[i]){
if(v[i]==fx||vis[v[i]])continue;
dis[v[i]]=dis[x]+w[i];
getdis(rt,v[i],x);
}
}
void getdeep(int rt,int x,int fx){
ADD_EDGE(x,rt,dis[x]);
for(int i=g[x];~i;i=nxt[i]){
if(v[i]==fx||vis[v[i]])continue;
dis[v[i]]=dis[x]+w[i];
getdeep(rt,v[i],x);
}
}
int belong[N];
void build(int x,int fx,int from,int d){
belong[x]=fx;
dis[x]=0; getdeep(x,x,fx); P[x].add(0);
if(fx){
dis[from]=d;
getdis(x,from,fx);
P[fx].add(Q[x].top());
}
vis[x]=1;
for(int i=g[x];~i;i=nxt[i])if(!vis[v[i]]){
root=0; sum=size[v[i]];
getroot(v[i],x);
build(root,x,v[i],w[i]);
}
ans.add(P[x].toptwo());
}
void change(int rt){
int a=P[rt].toptwo();
if(col[rt])P[rt].add(0);
else P[rt].del(0);
int b=P[rt].toptwo();
if(a!=b)ans.del(a),ans.add(b);
int x=rt;
for(int i=G[rt];~NXT[i];i=NXT[i]){
int y=V[NXT[i]],len=W[NXT[i]];
x=V[i];
a=Q[x].top();
if(col[rt])Q[x].add(len);
else Q[x].del(len);
b=Q[x].top();
if(a!=b){
int c=P[y].toptwo();
P[y].del(a),P[y].add(b);
int d=P[y].toptwo();
if(c!=d)ans.del(c),ans.add(d);
}
}
}
int n,m,a,b,c,x;
char op[10];
int main(){
scanf("%d",&n); init();
for(int i=1;i<=n;i++)col[i]=1;
for(int i=1;i<n;i++){
scanf("%d%d%d",&a,&b,&c);
add_edge(a,b,c);
add_edge(b,a,c);
}
memset(vis,0,sizeof(vis));
dp[root=0]=sum=n;
getroot(1,0);
build(root,0,0,0);
scanf("%d",&m);
int cnt=n;
while(m--){
scanf("%s",op);
if(op[0]=='A'){
if(cnt)printf("%d\n",ans.top());
else puts("They have disappeared.");
}else{
scanf("%d",&x);
col[x]^=1;
if(col[x])cnt++;
else cnt--;
change(x);
}
}return 0;
}

QTREE5

/*
题目大意:给出一棵树,边权均为1,一开始所有点都是白点,
要求维护两种操作:
1.颜色取反(黑白互变)
2.查询树上某点距离最近的白点与其距离
题解:我们维护重心树,对于每个重心维护分治层所有白点到其距离,
将其保存在对应方向子重心的优先队列中,
重心处另外维护一个队列表示每个子重心的最小答案,
考虑修改问题,等价于在处理优先队列的删除问题,
对于每个需要删除操作的优先队列,我们额外创建一个优先队列将删除元素加入其中,
当两个队列top元素相同时同时删去即可。
对于查询操作,我们沿着重心链用经过每个重心的最优值更新答案
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N=200010;
const int INF=0x3f3f3f3f;
int tot,g[N],nxt[N<<1],v[N<<1],w[N<<1];
int TOT,G[N],NXT[N*20],V[N*20],W[N*20];
void init(){
memset(g,-1,sizeof(g));tot=0;
memset(G,-1,sizeof(G));TOT=0;
}
void add_edge(int x,int y,int z){v[tot]=y,w[tot]=z,nxt[tot]=g[x],g[x]=tot++;}
void ADD_EDGE(int x,int y,int z){V[TOT]=y,W[TOT]=z,NXT[TOT]=G[x],G[x]=TOT++;}
int sum,root,size[N],dp[N],vis[N];
void getroot(int x,int fx){
size[x]=1; dp[x]=0;
for(int i=g[x];~i;i=nxt[i]){
if(!vis[v[i]]&&v[i]!=fx){
getroot(v[i],x);
size[x]+=size[v[i]];
dp[x]=max(dp[x],size[v[i]]);
}
}dp[x]=max(dp[x],sum-size[x]);
if(dp[x]<dp[root])root=x;
}
struct Que{
priority_queue<int,vector<int>,greater<int> > Q,D;
void clear(){
while(!Q.empty())Q.pop();
while(!D.empty())D.pop();
}
void add(int x){Q.push(x);}
void del(int x){D.push(x);}
int top(){
for(;;){
if(Q.empty())return INF;
else if(!D.empty()&&Q.top()==D.top())Q.pop(),D.pop();
else return Q.top();
}
}
}P[N],Q[N];
int dis[N],col[N];
void getdis(int rt,int x,int fx){
for(int i=g[x];~i;i=nxt[i]){
if(v[i]==fx||vis[v[i]])continue;
dis[v[i]]=dis[x]+w[i];
getdis(rt,v[i],x);
}
}
void getdeep(int rt,int x,int fx){
ADD_EDGE(x,rt,dis[x]);
for(int i=g[x];~i;i=nxt[i]){
if(v[i]==fx||vis[v[i]])continue;
dis[v[i]]=dis[x]+w[i];
getdeep(rt,v[i],x);
}
}
int belong[N];
void build(int x,int fx,int from,int d){
belong[x]=fx;
dis[x]=0; getdeep(x,x,fx);
if(fx){
dis[from]=d;
getdis(x,from,fx);
}
vis[x]=1;
for(int i=g[x];~i;i=nxt[i])if(!vis[v[i]]){
root=0; sum=size[v[i]];
getroot(v[i],x);
build(root,x,v[i],w[i]);
}
}
void change(int rt){
if(col[rt])P[rt].add(0);
else P[rt].del(0);
int x=rt;
for(int i=G[rt];~NXT[i];i=NXT[i]){
int y=V[NXT[i]],len=W[NXT[i]];
x=V[i];
int a=Q[x].top();
if(col[rt])Q[x].add(len);
else Q[x].del(len);
int b=Q[x].top();
if(a!=b)P[y].del(a),P[y].add(b);
}
}
int ask(int rt){
int ans=INF;
for(int i=G[rt];~i;i=NXT[i]){
int x=V[i],len=W[i];
int a=P[x].top();
if(a+len<ans)ans=a+len;
}
return ans;
}
int n,m,a,b,c,x,op;
int main(){
scanf("%d",&n); init();
for(int i=1;i<=n;i++)col[i]=0;
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
add_edge(a,b,1);
add_edge(b,a,1);
}
memset(vis,0,sizeof(vis));
dp[root=0]=sum=n;
getroot(1,0);
build(root,0,0,0);
scanf("%d",&m);
int cnt=0;
while(m--){
scanf("%d%d",&op,&x);
if(op==1){
if(cnt)printf("%d\n",ask(x));
else puts("-1");
}else{
col[x]^=1;
if(col[x])cnt++;
else cnt--;
change(x);
}
}return 0;
}

QTREE6

/*
题目大意:给出一棵树,一开始树上点均为黑色,要求维护操作
1.翻转某点的颜色(黑白互换),
2.查询某个点所在连通块(颜色相同则连通)大小
题解:我们分别维护黑点LCT和白点LCT,当一个点从黑变白时,
将其从黑点LCT中与父节点断开,然后在白点LCT中与父节点相连,
这样我们就保证了每个连通块在LCT中只有父节点是不同色的而其余一定是连通的。
考虑用LCT维护子树信息,根据在LCT上的连接情况,我们将点的儿子分为实儿子和虚儿子
实儿子是原本树结构上相连的点,实儿子的子树为实子树,虚儿子的子树为虚子树
x的LCT子树的信息和等于x的实儿子的LCT子树信息和加上x的虚子树的信息和加上x自己
在进行access操作时,我们会有更换一个点的x右儿子的操作,
这时我们要把x原来的右儿子的LCT子树信息加入x的虚子树信息,
把x的新的右儿子的LCT子树信息从x的虚子树信息中减去
同时在link x到y的时候,我们需要对y进行access再splay
这样就只会对y的虚子树信息和LCT子树信息产生影响,而不会影响到y的祖先节点
*/
#include <cstdio>
#include <algorithm>
#include <set>
const int N=100010;
using namespace std;
int n,m,i,k,x,y,c[N],fa[N],g[N],v[N<<1],nxt[N<<1],ed;
struct LCT{
int son[N][2],f[N],sum[N],s[N];
bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
void up(int x){sum[x]=1+sum[son[x][0]]+sum[son[x][1]]+s[x];}
void rotate(int x){
int y=f[x],w=son[y][1]==x;
son[y][w]=son[x][w^1];
if(son[x][w^1])f[son[x][w^1]]=y;
if(f[y]){
int z=f[y];
if(son[z][0]==y)son[z][0]=x;
if(son[z][1]==y)son[z][1]=x;
}f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
}
void splay(int x){
while(!isroot(x)){
int y=f[x];
if(!isroot(y)){
if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);
else rotate(y);
}rotate(x);
}up(x);
}
void access(int x){
for(int y=0;x;y=x,x=f[x]){
splay(x);
if(son[x][1])s[x]+=sum[son[x][1]];
if(son[x][1]=y)s[x]-=sum[y];
up(x);
}
}
int root(int x){access(x);splay(x);while(son[x][0])x=son[x][0];return x;}
void link(int x){access(fa[x]);splay(fa[x]);splay(x);son[fa[x]][1]=x;up(f[x]=fa[x]);}
void cut(int x){access(x);splay(x);f[son[x][0]]=0;son[x][0]=0;up(x);}
int ask(int x){splay(x=root(x));return sum[son[x][1]];}
}T[2];
void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x){
for(int i=g[x];i;i=nxt[i])if(v[i]!=fa[x]){
fa[v[i]]=T[c[v[i]]].f[v[i]]=x;
dfs(v[i]);
T[c[v[i]]].s[x]+=T[c[v[i]]].sum[v[i]];
}T[0].up(x),T[1].up(x);
}
void read(int&a){
char c;
while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';
while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';
}
int main(){
read(n);
for(i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x);
for(i=1;i<=n;i++)c[i]=0;
fa[1]=T[c[1]].f[1]=n+1;dfs(1);
T[c[1]].s[n+1]+=T[c[1]].sum[1];read(m);
while(m--){
read(k);read(x);
if(!k)printf("%d\n",T[c[x]].ask(x));
else T[c[x]].cut(x),T[c[x]^=1].link(x);
}return 0;
}

QTREE7

/*
题目大意:给出一棵树,树上每个点为黑色或者白色,每个点有个点权,
我们认为树上颜色相同并通过边连接的块为连通块
要求维护操作:
0.查询x所在连通块中权值最大的点
1.翻转某点的颜色(黑白互换)
2.改变某点的权值
题解:我们分别维护黑点LCT和白点LCT,当一个点从黑变白时,
将其从黑点LCT中与父节点断开,然后在白点LCT中与父节点相连,
这样我们就保证了每个连通块在LCT中只有父节点是不同色的而其余一定是连通的。
考虑用LCT维护子树信息,根据在LCT上的连接情况,我们将点的儿子分为实儿子和虚儿子
实儿子是原本树结构上相连的点,实儿子的子树为实子树,虚儿子的子树为虚子树
x的LCT子树的信息和等于x的实儿子的LCT子树信息和加上x的虚子树的信息和加上x自己
在进行access操作时,我们会有更换一个点的x右儿子的操作,
这时我们要把x原来的右儿子的LCT子树信息加入x的虚子树信息,
把x的新的右儿子的LCT子树信息从x的虚子树信息中减去
同时在link x到y的时候,我们需要对y进行access再splay
这样就只会对y的虚子树信息和LCT子树信息产生影响,而不会影响到y的祖先节点
因为要维护的信息是极值,因此我们要用multiset来维护虚子树信息
*/
#include <cstdio>
#include <algorithm>
#include <ctype.h>
#include <set>
const int N=100010;
using namespace std;
int n,m,k,x,y,c[N],fa[N],g[N],v[N<<1],nxt[N<<1],ed;
struct LCT{
int son[N][2],f[N],val[N],mx[N];
multiset<int> s[N];
bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
void up(int x){
mx[x]=val[x];
if(s[x].size())mx[x]=max(mx[x],*s[x].rbegin());
if(son[x][0])mx[x]=max(mx[x],mx[son[x][0]]);
if(son[x][1])mx[x]=max(mx[x],mx[son[x][1]]);
}
void rotate(int x){
int y=f[x],w=son[y][1]==x;
son[y][w]=son[x][w^1];
if(son[x][w^1])f[son[x][w^1]]=y;
if(f[y]){
int z=f[y];
if(son[z][0]==y)son[z][0]=x;
if(son[z][1]==y)son[z][1]=x;
}f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
}
void splay(int x){
while(!isroot(x)){
int y=f[x];
if(!isroot(y)){
if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);
else rotate(y);
}rotate(x);
}up(x);
}
void access(int x){
for(int y=0;x;y=x,x=f[x]){
splay(x);
if(son[x][1])s[x].insert(mx[son[x][1]]);
if(son[x][1]=y)s[x].erase(s[x].find(mx[y]));
up(x);
}
}
int root(int x){access(x);splay(x);while(son[x][0])x=son[x][0];return x;}
void link(int x){access(fa[x]);splay(fa[x]);splay(x);son[fa[x]][1]=x;up(f[x]=fa[x]);}
void cut(int x){access(x);splay(x);f[son[x][0]]=0;son[x][0]=0;up(x);}
int ask(int x){
int t=c[x]; splay(x=root(x));
return t==c[x]?mx[x]:mx[son[x][1]];
}
void modify(int x,int v){access(x),splay(x),val[x]=v,up(x);}
}T[2];
void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x){
for(int i=g[x];i;i=nxt[i])if(v[i]!=fa[x]){
fa[v[i]]=T[c[v[i]]].f[v[i]]=x;
dfs(v[i]);
T[c[v[i]]].s[x].insert(T[c[v[i]]].mx[v[i]]);
}T[0].up(x),T[1].up(x);
}
void read(int &x){
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
int t;if(ch=='-')t=-1,ch=getchar();else t=1;
for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
x=x*t;
}
int main(){
read(n);
for(int i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x);
for(int i=1;i<=n;i++)read(c[i]);
for(int i=1;i<=n;i++)read(T[0].val[i]),T[1].val[i]=T[0].val[i];
dfs(1); read(m);
while(m--){
read(k);read(x);
if(k==0)printf("%d\n",T[c[x]].ask(x));
else if(k==1)T[c[x]].cut(x),T[c[x]^=1].link(x);
else if(k==2)read(y),T[0].modify(x,y),T[1].modify(x,y);
}return 0;
}

SPOJ QTREE的更多相关文章

  1. QTREE 树链剖分---模板 spoj QTREE

    <树链剖分及其应用> 一文讲得非常清楚,我一早上就把他学会了并且A了这题的入门题. spoj QTREE 题目: 给出一棵树,有两种操作: 1.修改一条边的边权. 2.询问节点a到b的最大 ...

  2. SPOJ QTREE 系列解题报告

    题目一 : SPOJ 375 Query On a Tree http://www.spoj.com/problems/QTREE/ 给一个树,求a,b路径上最大边权,或者修改a,b边权为t. #in ...

  3. SPOJ QTREE Query on a tree 树链剖分+线段树

    题目链接:http://www.spoj.com/problems/QTREE/en/ QTREE - Query on a tree #tree You are given a tree (an a ...

  4. spoj QTREE - Query on a tree(树链剖分+线段树单点更新,区间查询)

    传送门:Problem QTREE https://www.cnblogs.com/violet-acmer/p/9711441.html 题解: 树链剖分的模板题,看代码比看文字解析理解来的快~~~ ...

  5. SPOJ QTREE Query on a tree ——树链剖分 线段树

    [题目分析] 垃圾vjudge又挂了. 树链剖分裸题. 垃圾spoj,交了好几次,基本没改动却过了. [代码](自带常数,是别人的2倍左右) #include <cstdio> #incl ...

  6. 【学术篇】SPOJ QTREE 树链剖分

    发现链剖这东西好久不写想一遍写对是有难度的.. 果然是熟能生巧吧.. WC的dalao们都回来了 然后就用WC的毒瘤题荼毒了我们一波, 本来想打个T1 44分暴力 然后好像是特判写挂了还是怎么的就只能 ...

  7. SPOJ QTree【树链剖分】

    一 题目 QTREE 二 分析 第一道树链剖分的题,写的好艰难啊. 题意还是比较好理解的,就是在树上操作. 对于修改,题中要求的是单点修改,就算是直接树上操作也是非常简单的. 对于查询,查询的时候,是 ...

  8. SPOJ QTREE Query on a tree

    题意:给一颗n个点的树,有两种操作CHANGE i ti : 把第i条边的权变为tiQUERY a b : 问点a 到 点b 之间的边的最大权 思路:树剖处理边权.由于是边,所以只需要把边权处理到子节 ...

  9. SPOJ QTREE Query on a tree --树链剖分

    题意:给一棵树,每次更新某条边或者查询u->v路径上的边权最大值. 解法:做过上一题,这题就没太大问题了,以终点的标号作为边的标号,因为dfs只能给点分配位置,而一棵树每条树边的终点只有一个. ...

随机推荐

  1. 【黑客免杀攻防】读书笔记14 - 面向对象逆向-虚函数、MFC逆向

    虚函数存在是为了克服类型域解决方案的缺陷,以使程序员可以在基类里声明一些能够在各个派生类里重新定义的函数. 1 识别简单的虚函数 代码示例: #include "stdafx.h" ...

  2. Win10搜索不能用

    使用win10进行搜索时,一直显示win10特色的滚动条,但就是检索不出东西,我的主要是检索不到windows的内容: (个人感觉使用win10检索网页内容不太专业,就关闭了Web搜索) 最后有发现网 ...

  3. 下一代Android打包工具,100个渠道包只需要10秒钟 https://github.com/mcxiaoke

    https://github.com/mcxiaoke/packer-ng-plugin https://github.com/Meituan-Dianping/walle https://githu ...

  4. 组件化表单解决方案AForm 1.3 发布

    v1.3 更新日志 输入控件的实现改为实例化模式,同类型多个输入控件在同一个表单不会冲突 输入控件实现了继承 可以使用AForm.create创建表单,和使用new AForm创建实例的参数和结果一样 ...

  5. CTF常用python库PwnTools的使用学习

    之前主要是使用zio库,对pwntools的了解仅限于DynELF,以为zio就可以取代pwntools.后来发现pwntools有很多的高级用法都不曾听说过,这次学习一下用法,希望可以在以后的exp ...

  6. appium----adb shell输入中文/Emoji表情符(ADBKeyBoard)

    前序 “adb shell input textyoyo“ 可以通过adb 输入英文的文本,由于不支持unicode编码,所以无法输入中文,github上有个国外的大神写了个ADBKeyBoard输入 ...

  7. 1926: [Sdoi2010]粟粟的书架

    大概就是分情况乱搞.. 经典维护二维前缀和暴力+莫队算法 垫底QAQ #include <bits/stdc++.h> using namespace std; namespace my_ ...

  8. python开发学习-day13(js、jQuery)

    s12-20160409-day13 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: ...

  9. 如何用正确的姿势编写jQuery插件

    在园子里有很多关于jQuery插件的文章,尤其 以下2篇文章: 不定义JQuery插件,不要说会JQuery jQuery插件开发精品教程,让你的jQuery提升一个台阶 这2位大神基础讲的很清楚,在 ...

  10. jenkins远程连接linux配置测试

    由于配置原因造成一下错误错误: jenkins.plugins.publish_over.BapPublisherException: Failed to connect session for co ...