您的位置:首页 > 其它

bzoj4448 [Scoi2015]情报传递(树链剖分+主席树)

2018-02-10 21:23 465 查看
题目链接

分析:

树上路径问题,显然可以链剖

看完题的我,大脑中浮现的想法↓:

对于每一个修改,在按照树链剖分建立的线段树上单点修改

记录这个点是什么时刻进行的修改

之后在询问的时候,我们只需查找区间内有多少时间戳小于等于“当前时刻-C-1”的结点即可

那么我们就可在线段树里套一个权值平衡树

这道题的题解:树链剖分+线段树+平衡树

我的MA呀,不觉得神烦吗???

实际上我们的问题就出在:查找区间内有多少时间戳小于等于“当前时刻-C-1”的结点

我们可以用主席树完成

我们把所有操作离线

按照在dfs序建立主席树,在主席树上处理完所有的修改操作

询问时直接利用树链剖分在主席树张区间查询即可

这样问题就迎刃而解了

tip

自从理解了线段树套平衡树之后,做题就很喜欢往这方面想。。。

在码的时候,突然有点绕不过来了:

主席树肯定建立在dfs序上的权值线段树

但是在询问的时候有时间限制,怎么破?

后来我才发现是我傻。。。

我们在主席树中记录的就是添加修改的时间点

只要查找区间内有多少时间戳小于等于“当前时刻-C-1”的结点即可

和询问出现的时间没有什么直接联系

(间接联系:时间已经体现在“当前时刻-C-1”的这个“当前时刻”中了)

一开始WA了一次

因为我对于没有修改的结点在主席树上没有插入,只有root的继承

后来改成对于没有修改的结点在主席树上插入m,就A了

#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

const int N=200005;
int n,m,root
,rt,cntC=0,cntQ=0,sz=0,tt;
int deep
,top
,pre
,num
,dfn
,clo=0,size
,son
;
struct point{
int ti,x,y;
};
point C
,Q
;
struct po{
int y,nxt;
};
po way
;
int st
,tot=0;
struct node{
int sum,l,r;
};
node t[N*20];

void add(int u,int w)
{
tot++;
way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
}

void dfs_1(int now,int fa,int dep)
{
deep[now]=dep;
pre[now]=fa;
size[now]=1;
int maxx=0;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa)
{
dfs_1(way[i].y,now,dep+1);
size[now]+=size[way[i].y];
if (size[way[i].y]>maxx)
maxx=size[way[i].y],son[now]=way[i].y;
}
}

void dfs_2(int now,int fa)
{
if (son[fa]!=now) top[now]=now;
else top[now]=top[fa];
dfn[++clo]=now; num[now]=clo;
if (son[now])
{
dfs_2(son[now],now);
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa&&way[i].y!=son[now])
dfs_2(way[i].y,now);
}
}

void insert(int &now,int l,int r,int x)
{
sz++;
t[sz]=t[now];
now=sz;
t[now].sum++;
if (l==r) return;
int mid=(l+r)>>1;
if (x<=mid) insert(t[now].l,l,mid,x);
else insert(t[now].r,mid+1,r,x);
}

void ask(int x,int y,int l,int r,int z)
{
if (l==r)
{
if (l<=z) tt+=t[y].sum-t[x].sum;
return;
}
int mid=(l+r)>>1;
int tmp=t[t[y].l].sum-t[t[x].l].sum;
if (z<=mid) ask(t[x].l,t[y].l,l,mid,z);
else tt+=tmp,ask(t[x].r,t[y].r,mid+1,r,z);
}

int query(int o)
{
int x=Q[o].x,y=Q[o].y;
int len=deep[x]+deep[y],ans=0;
int f1=top[x],f2=top[y];
while (f1!=f2)
{
if (deep[f1]<deep[f2]) swap(f1,f2),swap(x,y);
tt=0; ask(root[num[f1]-1],root[num[x]],1,m,Q[o].ti);
ans+=tt;
x=pre[f1]; f1=top[x];
}
if (deep[x]>deep[y]) swap(x,y);
tt=0; ask(root[num[x]-1],root[num[y]],1,m,Q[o].ti);
ans+=tt;
len=len-2*deep[x]+1;

printf("%d %d\n",len,ans);
}

int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
if (!x) rt=i;
else add(x,i);
}

dfs_1(rt,0,1);
dfs_2(rt,0);

scanf("%d",&m);
int opt,x,y,c;
for (int i=1;i<=m;i++)
{
scanf("%d",&opt);
if (opt==1)
{
cntQ++;
scanf("%d%d%d",&Q[cntQ].x,&Q[cntQ].y,&Q[cntQ].ti);
Q[cntQ].ti=i-Q[cntQ].ti-1;
} else {
scanf("%d",&x);
C[x].ti=i;
}
}

for (int i=1;i<=n;i++)
{
root[i]=root[i-1];
if (C[dfn[i]].ti!=0) insert(root[i],1,m,C[dfn[i]].ti);
else insert(root[i],1,m,m);   //插入m
}

for (int i=1;i<=cntQ;i++) query(i);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: