首页 > 其他分享 >P9058 [Ynoi2004] rpmtdq

P9058 [Ynoi2004] rpmtdq

时间:2024-07-14 09:51:29浏览次数:9  
标签:P9058 sz int rpmtdq Ynoi2004 vec mx top dis

前言

开个巨坑,可能以后会三四天一更?

分析

路径考虑点分治,以下的 \(dis\) 皆为确定根节点的。
以下点对 \((i,j)\) 都钦定 \(dis_i\le dis_j\)
注意到很多点对对答案是无用的,条件为若点对 \((i,j)\)存在 \(k\in[l+1,r-1]\),满足 \(dis_i>=dis_k\ or\ dis_j\ge dis_k\),则点对 \((i,j)\)无用,又称 \((i,k)\ or\ (k,j)\) 支配了 \((i,j)\)。
问题转化为了找到不被支配的点对 \((i,j)\),条件由定义得 \(max(dis_i,dis_j)<\min\limits_{k=l+1}^{r-1} dis_k\),但是点对数量为平方级别,支配点对常常与前后继有关,我们不妨大胆猜测,对于点 \(i\),只有 \(i\) 的前继与后继是有用的点对,小心求证一下。
令 \(pre\) 为 \(i\) 满足条件的前继,\(j\) 为小于 \(pre\) 满足条件的一点。
考虑反证法,若点对 \((j,i)\) 未被支配。
有 \(dis_j,dis_{pre}<dis_i\)。

\[dis_{j,pre}=dis_{j}+dis_{pre}<dis_j+dis_i=dis_{i,j} \]

发现 \((i,j)\) 被 \((j,pre)\) 支配。
以上为前继证明,后继同理。
考虑代码实现。
由上述条件可以维护一个 \(dis\) 单调不升的栈,依次插入有序标号实现。
最后统计答案二维数点即可。

代码

const int N=2e5+10;

const int M=1e6+10;

int tot,len,top,rt,n,q,fa[N][20],sz[N],mx[N];
bool vis[N];
ll ans[M];
vector <PII> Q[N],G[N];
vector <int> Point[N];
pair<int,ll> vec[N],sta[N];

void getroot(int u,int par){
	sz[u]=1,mx[u]=0;
	for(auto [v,w]:G[u]){
		if(v==par||vis[v])
			continue;
		getroot(v,u);
		sz[u]+=sz[v],mx[u]=max(mx[u],sz[v]);
	}
	mx[u]=max(mx[u],tot-sz[u]);
	if(mx[u]<mx[rt]) rt=u;
}

void add(int u,int par,ll dep){
	vec[++len]={u,dep};
	for(auto [v,w]:G[u]){
		if(v==par||vis[v])
			continue;
		add(v,u,dep+w);
	}
}

void dfs(int u){
	vis[u]=1,len=0;
	for(auto [v,w]:G[u]){
		if(vis[v]) continue;
		add(v,0,w);
	}
	vec[++len]={u,0};
	sort(vec+1,vec+len+1);
	top=0;
	for(int i=1;i<=len;i++){
		while(top&&sta[top].se>vec[i].se) 
			top--;
		if(top) Point[sta[top].fi].pb(vec[i].fi);
		sta[++top]=vec[i];
	}
	top=0;
	for(int i=len;i>=1;i--){
		while(top&&sta[top].se>vec[i].se) 
			top--;
		if(top) Point[vec[i].fi].pb(sta[top].fi);
		sta[++top]=vec[i];
	}
	for(auto [v,w]:G[u]){
		if(vis[v]) continue;
		tot=sz[v],rt=0;
		getroot(v,0);
		dfs(rt);
	}
}

struct BIT{
	ll t[N];
	BIT(){memset(t,0x3f,sizeof t);}
	void add(int x,ll v){
		while(x<=n) t[x]=min(t[x],v),x+=(x&(-x));
	}
	ll query(int x){
		ll res=0x3f3f3f3f3f3f3f3f;
		while(x) res=min(res,t[x]),x-=(x&(-x));
		return res;
	}
}T;

struct LCA{
	ll dis[N];
	int dfn[N],mn[N][19],ti;
	void dfs(int u,int par){
		mn[(dfn[u]=++ti)][0]=par;
		for(auto [v,w]:G[u])
			if(v!=par)
				dis[v]=dis[u]+w,dfs(v,u);
	}
	int MIN(int x,int y){
		return dfn[x]<dfn[y]?x:y;
	}
	void init(){
		for(int i=1;i<=__lg(n);i++){
			for(int j=1;j+(1<<i)-1<=n;j++){
				mn[j][i]=MIN(mn[j][i-1],mn[j+(1<<(i-1))][i-1]);
			}
		}
	}
	int getlca(int u,int v){
		if(u==v) return u;
		if((u=dfn[u])>(v=dfn[v])) swap(u,v);
		int d=__lg(v-u++);
		return MIN(mn[u][d],mn[v-(1<<d)+1][d]);
	}
	ll getdis(int u,int v){
		return dis[u]+dis[v]-2*dis[getlca(u,v)];
	}
}F;

void Main(){
	n=rd;
	for(int i=1;i<n;i++){
		int u=rd,v=rd,w=rd;
		G[u].pb({v,w});
		G[v].pb({u,w});
	}
	tot=n,mx[0]=2e9;
	getroot(1,0);
	dfs(rt);
	F.dfs(1,0),F.init();
	q=rd;
	for(int i=1;i<=q;i++){
		int l=rd,r=rd;
		Q[l].pb({r,i});
	}
	
	for(int i=n;i;i--){
		for(int j:Point[i]) T.add(j,F.getdis(i,j));
		for(auto [x,id]:Q[i])
			ans[id]=T.query(x);
	}
	for(int i=1;i<=q;i++){
		if(ans[i]==0x3f3f3f3f3f3f3f3f)
			ans[i]=-1;
		printf("%lld\n",ans[i]);
	}
}

标签:P9058,sz,int,rpmtdq,Ynoi2004,vec,mx,top,dis
From: https://www.cnblogs.com/smilemask/p/18301123

相关文章

  • P9058 [Ynoi2004] rpmtdq 题解
    Description给定一棵有边权的无根树,需要回答一些询问。定义\(\texttt{dist(i,j)}\)代表树上点\(i\)和点\(j\)之间的距离。对于每一组询问,会给出\(l,r\),你需要输出\(\min(\texttt{dist(i,j)})\)其中\(l\leqi<j\leqr\)。\(n\leq2\times10^5\),\(q\leq10^6\),\(1\l......
  • 洛谷 P9058 [Ynoi2004] rpmtdq
    洛谷传送门类比P9062[Ynoi2002]AdaptiveHsearch&Lsearch处理区间最近点对的思路,尝试只保留可能有贡献的点对。处理树上路径容易想到点分治。设点\(u\)到分治中心的距离为\(a_u\)。我们有\(\text{dis}(u,v)\lea_u+a_v\)。考虑一个点对\((u,v)\)什么时候一定没......
  • [Ynoi2004] rpmtdq 题解
    人生第一发\(Ynoi\)的题,写一篇题解庆祝一下传送门我们可以发现,对于二元组\((x,y)\),若存在一个\(dist(i,j)\ledist(x,y),x<i<j<y\)那么答案肯定不是二元组\((x,y)\)我们可以考虑把这些肯定不是的点剔除掉考虑怎么找,我们可以先点分治,求出每个点......