4.18 省选模拟赛 消息传递 树剖 倍增 线段树维护等比数列

avatar
avatar

把树剖和倍增 线段树的联系诠释的很完美。

题目意思:自行理解。

做法:设两个点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 排序的时候别打错。

原文地址:https://www.cnblogs.com/chdy/p/12748555.html