4.18 省选模拟赛 消息传递 树剖 倍增 线段树维护等比数列
把树剖和倍增 线段树的联系诠释的很完美。
题目意思:自行理解。
做法:设两个点x,y x能挡住y 且在k点处 那么至少的得到一个式子 tx+dx-dk<tx+dy-dk
从这个式子中可以发现 按tx+dx这个排序 前面的可以有可能挡住后面的 同时相等时题目中的要求小的编号优先。
我们考虑一个点挡住当前的这个点的去路 设sx表示x这个点当前不能通过的时间 那么当 dy-dx+ty>=sx时可以通过反之不行。且x时y到根的路径上的点。
将等式变形 容易发现 dy+ty>=sx+dx.
每次其实就是链上查询一点 然后链上赋值操作。
考虑前者 树剖向上跳 然后发现当前到top点之间不能直接跳之后换倍增跳。(树剖与倍增的一个小trick。
考虑后者 赋值操作可以发现时一个等差数列 但是还不能直接赋值 因为还有基础数值深度。
此时 赋值的时候 可以发现可以直接赋上深度就可以解决这个问题了。那么等差数列的公差就就变成了2.
线段树+树剖维护等差数列的修改即可。
真的巧妙。
const int MAXN=200010;
int n,Q,len,rt,cnt;
int f[MAXN][20],Log[MAXN],son[MAXN],id[MAXN],sz[MAXN],top[MAXN];
int lin[MAXN],ver[MAXN],nex[MAXN],pos[MAXN],d[MAXN],ans[MAXN];
struct wy{int x,ti,id;}t[MAXN];
struct jl{int l,r,sum,tag,d;}s[MAXN<<2];
inline int cmp(wy a,wy b){return a.ti+d[a.x]==b.ti+d[b.x]?a.x<b.x:a.ti+d[a.x]<b.ti+d[b.x];}
inline void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
inline void dfs(int x,int fa)
{
sz[x]=1;f[x][0]=fa;
d[x]=d[fa]+1;
rep(1,Log[d[x]],i)f[x][i]=f[f[x][i-1]][i-1];
go(x)
{
dfs(tn,x);
sz[x]+=sz[tn];
if(sz[tn]>sz[son[x]]||!son[x])son[x]=tn;
}
}
inline void dp(int x,int fa)
{
id[x]=++cnt;pos[cnt]=x;top[x]=fa;
if(!son[x])return;
dp(son[x],fa);
go(x)if(tn!=son[x])dp(tn,tn);
}
inline void pushup(int p){sum(p)=max(sum(zz),sum(yy));}
inline void build(int p,int l,int r)
{
l(p)=l;r(p)=r;
if(l==r)return;
int mid=(l+r)>>1;
build(zz,l,mid);
build(yy,mid+1,r);
}
inline void push(int p,int x,int w)
{
d(p)=w;tag(p)=x;
sum(p)=x+(r(p)-l(p))*w;
}
inline void pushdown(int p)
{
int mid=(l(p)+r(p))>>1;
push(zz,tag(p),d(p));
push(yy,tag(p)+(mid-l(p)+1)*d(p),d(p));
d(p)=tag(p)=0;
}
inline void change(int p,int l,int r,int x,int w)
{
if(l==l(p)&&r==r(p)){push(p,x,w);return;}
int mid=(l(p)+r(p))>>1;
if(tag(p)||d(p))pushdown(p);
if(r<=mid)change(zz,l,r,x,w);
else
{
if(l>mid)change(yy,l,r,x,w);
else change(zz,l,mid,x,w),change(yy,mid+1,r,x+w*(mid-l+1),w);
}
pushup(p);
}
inline int ask(int p,int l,int r)
{
if(l<=l(p)&&r>=r(p))return sum(p);
int mid=(l(p)+r(p))>>1,cnt=0;
if(d(p)||tag(p))pushdown(p);
if(l<=mid)cnt=max(cnt,ask(zz,l,r));
if(r>mid)cnt=max(cnt,ask(yy,l,r));
return cnt;
}
int main()
{
freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
get(n);get(Q);
rep(1,n,i)add(read(),i),Log[(i+1)]=Log[(i+1)>>1]+1;
dfs(0,0);dp(0,0);
build(1,1,cnt);change(1,1,1,INF,0);
rep(1,Q,i){int get(x);t[i]=(wy){x,read(),i};}
sort(t+1,t+1+Q,cmp);
rep(1,Q,i)
{
int x=t[i].x;
while(ask(1,id[top[x]],id[x])<=t[i].ti+d[t[i].x])x=f[top[x]][0];
fep(Log[d[x]],0,j)
{
if(d[f[x][j]]<d[top[x]])continue;
if(ask(1,id[f[x][j]],id[x])<=t[i].ti+d[t[i].x])x=f[x][j];
}
int cc;if(ask(1,id[x],id[x])<=t[i].ti+d[t[i].x])cc=f[x][0];else cc=x;
ans[t[i].id]=(d[t[i].x]-d[cc])*2+t[i].ti;
x=t[i].x;
//cout<<cc<<endl;
while(top[cc]!=top[x])
{
change(1,id[top[x]],id[x],t[i].ti+d[t[i].x]-d[cc]+d[top[x]]-d[cc]+d[top[x]],2);
//cout<<t[i].ti+d[t[i].x]-d[cc]+d[top[x]]-d[cc]+d[top[x]]<<endl;
x=f[top[x]][0];
}
if(x!=cc)change(1,id[cc]+1,id[x],t[i].ti+d[t[i].x]+2,2);
//cout<<t[i].ti+d[t[i].x]+2<<endl;
}
rep(1,Q,i)printf("%d ",ans[i]);
return 0;
}
坑点 Log数组要到n+1 排序的时候别打错。