题目描述
Bob有一棵nn 个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。
定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。
Bob可能会进行这几种操作:
1 x
把点xx 到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y
求xx 到yy 的路径的权值。
3 x
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行mm 次操作
输入输出格式
输入格式:
第一行两个数n,mn,m 。
接下来n-1n−1 行,每行两个数a,ba,b ,表示aa 与bb 之间有一条边。
接下来mm 行,表示操作,格式见题目描述
输出格式:
每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值
输入输出样例
输入样例#1:
5 6
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5
输出样例#1:
3
4
2
2
说明
n<=1e5,m<=1e5 n <= 1 e 5 , m <= 1 e 5
时间限制:1s
空间限制:128MB
看到这道题,咦?access?
仔细思考一波,你发现,他可以维护一个当前节点到跟节点的颜色种数,然后利用 ans=col(x)+col(y)−2col(lca(x,y))+1 a n s = c o l ( x ) + c o l ( y ) − 2 c o l ( l c a ( x , y ) ) + 1 求解,然后你可以利用access和线段树来完成它,但是会有许多坑的地方——
#include
using namespace std;
#define MN 100010
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
//fa1 为 袁术 fa2 为 LCT
struct lll{
int to,ne;
}a[MN*2];
int n,q,x,y,lk[MN],h[MN],up[MN][40],fa2[MN],ch[MN][2],zs[MN],vis[MN],dep[MN],tag[MN*20],
dfn[MN],tot,cnt,opt,mm[MN*20];
void link(int x,int y)
{
a[++cnt].to=y,a[cnt].ne=h[x],h[x]=cnt;
}
void dfs(int u)
{
lk[u]=++tot,dfn[tot]=u;
for(int i=1;i<=29;i++) up[u][i]=up[up[u][i-1]][i-1];
for(int i=h[u];i;i=a[i].ne)
{
int v=a[i].to;
if(!vis[v])
{
up[v][0]=u;dep[v]=dep[u]+1;fa2[v]=u;vis[v]=1;dfs(v);
}
}
zs[u]=tot;
}
int lca(int u,int v)
{
if(dep[u]for(int i=29;i>=0;i--)
if(dep[up[u][i]]>=dep[v]) u=up[u][i];// wrong 1st 这里写成了 dep[up[u][i]]>dep[v]
if(u==v) return u;
for(int i=29;i>=0;i--)
if(up[u][i]!=up[v][i]) u=up[u][i],v=up[v][i];
return up[u][0];
}
void pushup(int rt)
{
mm[rt]=max(mm[rt<<1],mm[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l==r) {mm[rt]=dep[dfn[l]];return ;}
int mid=(r+l)>>1;
build(lson);build(rson);pushup(rt);
}
void pushdown(int rt)
{
if(tag[rt])
{
mm[rt<<1]+=tag[rt],tag[rt<<1]+=tag[rt];
mm[rt<<1|1]+=tag[rt],tag[rt<<1|1]+=tag[rt];
tag[rt]=0;
}
pushup(rt);
}
void update(int L,int R,int l,int r,int rt,int c)
{
if(L<=l&&r<=R) {mm[rt]+=c;tag[rt]+=c;return ;}
int mid=(r+l)>>1;pushdown(rt);
if(L<=mid) update(L,R,lson,c);
if(midint ask(int pos,int l,int r,int rt)
{
if(l==r) {return mm[rt];}
int mid=(r+l)>>1;pushdown(rt);
if(pos<=mid) return ask(pos,lson);
else return ask(pos,rson);
}
int maxx(int L,int R,int l,int r,int rt)
{
int ans=-2100000000;
if(L<=l&&r<=R) return mm[rt];// wrong 2ed 错写为了 L<=r&&r<=R
int mid=(r+l)>>1;pushdown(rt);
if(L<=mid) ans=max(ans,maxx(L,R,lson));
if(midreturn ans;
}
bool nroot(int x)
{
return ch[fa2[x]][1]==x||ch[fa2[x]][0]==x;
}
void rotate(int x)
{
int old=fa2[x],oldf=fa2[old],which=(ch[old][1]==x),w=ch[x][!which];
if(nroot(old)) ch[oldf][ch[oldf][1]==old]=x;
ch[x][!which]=old;ch[old][which]=w;
if(w) fa2[w]=old;
fa2[x]=oldf,fa2[old]=x;
}
void splay(int x)
{
while(nroot(x))
{
if(nroot(fa2[x]))
rotate(((ch[fa2[x]][1]==x)^(ch[fa2[fa2[x]]][1]==fa2[x]))?x:fa2[x]);
rotate(x);
}
}
int frt(int x)
{
while(ch[x][0]) x=ch[x][0];
return x;
}
void access(int x)
{
for(int y=0;x;x=fa2[y=x])// wrong 3ed 这里很关键,你修改的是原树的子树所以要findroot一下!
{
int w;
splay(x);if(ch[x][1]) w=frt(ch[x][1]) ,update(lk[w],zs[w],1,n,1,1);
ch[x][1]=y;if(y) w=frt(y) ,update(lk[w],zs[w],1,n,1,-1);
}
}
int gsum(int x)
{
int ans;
ans=ask(x,1,n,1);
return ans;
}
void Prt(int l,int r,int rt)
{
if(l==r) {printf("%d",mm[rt]);return ;}
int mid=(l+r)>>1;pushdown(rt);
Prt(lson);Prt(rson);
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i"%d%d",&x,&y),link(x,y),link(y,x);
vis[1]=dep[1]=1;dfs(1);build(1,n,1);
//Prt(1,n,1);
//for(int i=1;i<=n;i++) printf("%d ",dfn[i]);
//printf("%d",gsum(lk[3]));
while(q--)
{
scanf("%d" ,&opt);
if(opt==1) scanf("%d",&x),access(x);
if(opt==2) scanf("%d%d",&x,&y),
printf("%d\n",(gsum(lk[y])+gsum(lk[x])-2*gsum(lk[lca(x,y)])+1));
if(opt==3) scanf("%d",&x),printf("%d\n",maxx(lk[x],zs[x],1,n,1));
//Prt(1,n,1);
}
return 0;
}