传送门
Description
在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个结点,可以打多次标记。)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖先)你能帮帮他吗?
Input
输入第一行两个正整数N和Q分别表示节点个数和操作次数接下来N-1行,每行两个正整数u,v(1≤u,v≤n)表示u到v有一条有向边接下来Q行,形如“opernum”oper为“C”时表示这是一个标记操作,oper为“Q”时表示这是一个询问操作对于每次询问操作,1 ≤ N, Q ≤ 100000。
Output
输出一个正整数,表示结果
Sample Input
5 5
1 2
1 3
2 4
2 5
Q 2
C 2
Q 2
Q 5
Q 3
Sample Output
1
2
2
1
HINT
新加数据9组(By HFLSyzx ),未重测–2016.8.2
题解
这道题可以用树链剖分做,然而我的方法非常愚蠢,速度排到了bzoj倒数第5QAAAQ
就当练习一次好了
看了别人的题解,感觉自己辣鸡到爆
正常的做法:线段树存储离该点最近的标记过的祖先节点,直接查询,取最优解
zz的做法:线段树存储该点管辖范围中有无标记过的节点,查询时进行各种沙茶特判,自带大常数
自己又重新写了一遍,速度快了不少
新写的代码中Add函数的最后更新当前点的时候并没有取max,而是优先使用右儿子的数据,这是因为在树链剖分得到的dfs序中,同一条链是连续的,所以更深的节点应当在右侧;如果一个节点的左右儿子不是同一条链上的也没有关系,因为我们在查询的时候根本就用不到这样的节点上的数据。
QAQ::codecmp(辣鸡的代码,优良的代码):
辣鸡的代码:
#include<cstdio>
const int INF=1e9;
const int N=1e5+10;
struct edge
{
int nxt,to;
}a[N<<1];
bool t[N<<2];
int head[N],deep[N],f[N],sz[N],son[N],top[N],pos[N],id[N];
int n,q,x,y,num,tot;
inline int max(int a,int b){return a>b?a:b;}
inline int min(int a,int b){return a<b?a:b;}
inline void add(int x,int y)
{
a[++num].nxt=head[x],a[num].to=y,head[x]=num;
a[++num].nxt=head[y],a[num].to=x,head[y]=num;
}
void dfs(int now,int fa,int depth)
{
deep[now]=depth;
f[now]=fa;
sz[now]=1;
int tmp=-INF;
for(int i=head[now];i;i=a[i].nxt)
if(a[i].to!=fa)
{
dfs(a[i].to,now,depth+1);
sz[now]+=sz[a[i].to];
if(sz[a[i].to]>tmp) tmp=sz[a[i].to],son[now]=a[i].to;
}
}
void dfs2(int now,int high)
{
top[now]=high;
pos[now]=++tot;
id[tot]=now;
if(son[now]) dfs2(son[now],high);
for(int i=head[now];i;i=a[i].nxt)
if(a[i].to!=f[now]&&a[i].to!=son[now]) dfs2(a[i].to,a[i].to);
}
void Add(int p,int l,int r,int now)
{
if(l==r)
{
t[now]=1;
return;
}
int mid=(l+r)>>1;
if(p<=mid) Add(p,l,mid,now<<1);
else Add(p,mid+1,r,now<<1|1);
t[now]=1;
}
bool Ask(int L,int R,int l,int r,int now)
{
if(L<=l&&r<=R) return t[now];
int mid=(l+r)>>1;
if(L<=mid&&Ask(L,R,l,mid,now<<1)) return 1;
if(R>mid&&Ask(L,R,mid+1,r,now<<1|1)) return 1;
return 0;
}
int ask(int p,int wh,int l,int r,int now)
{
if(l==r) return id[l];
int mid=(l+r)>>1;
if(!wh)
{
if(p>mid&&Ask(mid+1,p,1,n,1)) return ask(p,wh,mid+1,r,now<<1|1);
else return ask(p,wh,l,mid,now<<1);
}
if(p<=mid&&Ask(p,mid,1,n,1)) return ask(p,wh,l,mid,now<<1);
return ask(p,wh,mid+1,r,now<<1|1);
}
int askpath(int x)
{
int L,R;
L=min(pos[x],pos[top[x]]);
R=max(pos[x],pos[top[x]]);
while(!Ask(L,R,1,n,1))
{
x=f[top[x]];
L=min(pos[x],pos[top[x]]);
R=max(pos[x],pos[top[x]]);
}
if(pos[x]<=pos[top[x]]) return ask(pos[x],1,1,n,1);
return ask(pos[x],0,1,n,1);
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<n;i++)
scanf("%d%d",&x,&y),add(x,y);
dfs(1,0,1);
dfs2(1,1);
Add(1,1,n,1);
while(q--)
{
char c=getchar();
while(c!='Q'&&c!='C') c=getchar();
scanf("%d",&x);
if(c=='Q') printf("%d\n",askpath(x));
else Add(pos[x],1,n,1);
}
return 0;
}
优良的代码:
#include<cstdio>
const int INF=1e9;
const int N=1e5+10;
struct edge
{
int nxt,to;
}a[N<<1];
int t[N<<2];
int head[N],sz[N],pos[N],top[N],f[N],son[N],deep[N];
int n,q,x,y,num,tot;
inline void add(int x,int y)
{
a[++num].nxt=head[x],a[num].to=y,head[x]=num;
a[++num].nxt=head[y],a[num].to=x,head[y]=num;
}
void dfs(int now)
{
sz[now]=1;
for(int i=head[now];i;i=a[i].nxt)
if(a[i].to!=f[now])
{
f[a[i].to]=now;
deep[a[i].to]=deep[now]+1;
dfs(a[i].to);
sz[now]+=sz[a[i].to];
if(sz[a[i].to]>sz[son[now]]) son[now]=a[i].to;
}
}
void dfs2(int now)
{
pos[now]=++tot;
if(son[now]) top[son[now]]=top[now],dfs2(son[now]);
for(int i=head[now];i;i=a[i].nxt)
if(a[i].to!=f[now]&&a[i].to!=son[now]) top[a[i].to]=a[i].to,dfs2(a[i].to);
}
void Add(int p,int l,int r,int now,int num)
{
if(l==r)
{
t[now]=num;
return;
}
int mid=(l+r)>>1;
if(p<=mid) Add(p,l,mid,now<<1,num);
else Add(p,mid+1,r,now<<1|1,num);
t[now]=t[now<<1|1]?t[now<<1|1]:t[now<<1];
}
int ask(int L,int R,int l,int r,int now)
{
if(L<=l&&r<=R) return t[now];
int mid=(l+r)>>1,ans=0,tmp;
if(L<=mid)
{
tmp=ask(L,R,l,mid,now<<1);
if(deep[ans]<deep[tmp]) ans=tmp;
}
if(R>mid)
{
tmp=ask(L,R,mid+1,r,now<<1|1);
if(deep[ans]<deep[tmp]) ans=tmp;
}
return ans;
}
inline int askpath(int x)
{
int L=pos[top[x]],R=pos[x],ans;
while(!(ans=ask(L,R,1,n,1)))
{
x=f[top[x]];
L=pos[top[x]];
R=pos[x];
}
return ans;
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<n;i++)
scanf("%d%d",&x,&y),add(x,y);
deep[1]=1;dfs(1);
top[1]=1;dfs2(1);
Add(1,1,n,1,1);
while(q--)
{
char c=getchar();
while(c!='Q'&&c!='C') c=getchar();
scanf("%d",&x);
if(c=='Q') printf("%d\n",askpath(x));
else Add(pos[x],1,n,1,x);
}
return 0;
}