首页 > 其他分享 >P9058 [Ynoi2004] rpmtdq 与 P9678 [ICPC2022 Jinan R] Tree Distance

P9058 [Ynoi2004] rpmtdq 与 P9678 [ICPC2022 Jinan R] Tree Distance

时间:2024-07-29 21:33:17浏览次数:6  
标签:rt P9058 支配 le ICPC2022 int rpmtdq operatorname dis

思路:

注意到点对数量有 \(N^2\) 个,考虑丢掉一些无用的点对。

对于点对 \((x_1,y_1),(x_2,y_2)\),满足 \(x_1 \le x_2 < y_2 \le y_1\),即区间 \([x_2,y_2]\) 被 \([x_1,y_1]\) 包含,此时满足若询问到了 \([x_1,y_1]\),则一定会询问到 \([x_2,y_2]\)。

若满足 \(\operatorname{dis}(x_1,y_1) \ge \operatorname{dis}(x_2,y_2)\),那么此时可以将 \((x_1,y_1)\) 舍弃,因为若要用 \((x_1,y_1\)) 的贡献,不如直接去看 \((x_2,y_2)\) 的贡献,毕竟 \((x_1,y_1)\) 的贡献一定不会比 \((x_2,y_2)\) 更优。

那么我们可以定义若两个点对 \((x_1,y_1),(x_2,y_2)\) 满足以下条件,则称 \((x_1,y_1)\) 被 \((x_2,y_2)\) 支配

  • \(x_1 \le x_2 < y_2 \le y_1\)。

  • \(\operatorname{dis}(x_1,y_1) \ge \operatorname{dis}(x_2,y_2)\)。

此时定义一个支配点对满足没有被任何一个点对支配,即我们需要找出所有的支配点对来计算贡献。

注意到是一个树上点对距离问题,考虑点分治解决。

令当前分治重心为 \(rt\),对于点 \(v\),令 \(S_v\) 表示当前联通块中所有满足 \(\operatorname{dis}(i,rt) \le \operatorname{dis}(v,rt)\) 的 \(i\) 组成的一个集合

那么可以与 \(v\) 组成支配点对的点一定是 \(S_v\) 中 \(v\) 的前驱后继,即 \(S_v\) 中 \(<v\) 中最大的数和 \(>v\) 中最小的数

简单证一下,设 \(S_v\) 中 \(v\) 的前驱为 \(u\):

  • 有 \(\operatorname{dis}(i,u) \le \operatorname{dis}(i,rt) + \operatorname{dis}(u,rt) \le \operatorname{dis}(i,rt) + \operatorname{dis}(v,rt) = \operatorname{dis}(i,v)\),即 \(\operatorname{dis}(i,u) \le \operatorname{dis}(i,v)\)。

  • 注意到此时 \(i < u < v\) 或 \(u < v < i\),即 \((i,v)\) 被 \((i,u)\) 支配或 \((u,i)\) 被 \((v,i)\) 支配

  • 那么只有当 \(i=u\) 时,\((u,u)\) 点对不存在,\((u,v)\) 不会被其它 \(S_v\) 中的点对支配。

后继情况类似,就不多说了。

然后考虑如何快速找到支配点对,直接按照上面的方法找 \(S_v\),复杂度肯定是 \(O(N^2)\) 起步,考虑优化。

首先对于整个联通块的所有点,按照点的编号排序升序,然后维护一个 \(\operatorname{dis}(i,rt)\) 不降的单调栈

那么有一个性质是,对于被点 \(i\) 弹出去的点 \(u\),肯定满足 \(i\) 是 \(u\) 后面第一个小于等于 \(\operatorname{dis}(u,rt)\) 的点且编号最小,即 \(i\) 是 \(S_u\) 中 \(u\) 的前驱;然后再倒着降序做一遍单调栈后继即可。

此时我们来估算一下支配点对的数量,每个点最多被 \(\log N\) 个分治重心包含,每次包含最多增加 \(2\) 对支配点对,即总支配点对的数量为 \(N \log N\) 左右。

现在求出了全部的支配点对,即有贡献的点对,现在考虑如何求被一个区间包含的所有支配点对的最小贡献值,可以在线使用树套树,但是没必要。

考虑离线使用扫描线算法,因为树状数组不好维护后缀最值,考虑倒着扫左端点,然后对于每个点对,在左端点处将右端点贡献加入进去;那么对于一个在左端点的询问,就是一个前缀最小值。

时间复杂度为 \(O(N \log^2 N + Q \log N)\)。

完整代码:

#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
const ll N=2e5+10,M=1e6+10,INF=1e18; 
bool Begin;
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
inline void write(ll x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
}
int n,q;
ll ans[M];
vector<int> G[N];
vector<pair<int,ll>> E[N],Q[N];
void add(int u,int v,int w){
	E[u].push_back({v,w});
	E[v].push_back({u,w});
}
namespace Lowbit{
	ll a[N];
	inline void init(){
		for(int i=1;i<=n;i++)
		  a[i]=INF;
	}
	inline void add(int x,ll w){
		for(int i=x;i<=n;i+=lowbit(i))
		  a[i]=min(a[i],w);
	}
	inline ll query(int x){
		ll ans=INF;
		for(int i=x;i;i-=lowbit(i))
		  ans=min(ans,a[i]);
		return ans;
	}	
};
namespace LCA{
	int p[N],t[N],z[N],d[N],fa[N];
	ll dep[N];
	inline void dfs1(int u,int f){
		p[u]=1;
		for(auto t:E[u]){
			int v=t.first,w=t.second;
			if(v==f)
			  continue;
			dep[v]=dep[u]+w;
			d[v]=d[u]+1;
			fa[v]=u;
			dfs1(v,u);
			p[u]+=p[v];
			if(p[v]>p[z[u]])
			  z[u]=v;
		}
	}
	inline void dfs2(int u,int k){
		t[u]=k;
		if(!z[u])
		  return ;
		dfs2(z[u],k);
		for(auto t:E[u]){
			int v=t.first;
			if(v==fa[u]||v==z[u])
			  continue;
			dfs2(v,v);
		}
	}
	inline int Lca(int u,int v){
		while(t[u]!=t[v]){
			if(d[t[u]]<d[t[v]])
			  swap(u,v);
			u=fa[t[u]];
		}
		return d[u]<d[v]?u:v;
	}
	inline ll dis(int u,int v){
		return dep[u]+dep[v]-(dep[Lca(u,v)]<<1ll);
	}
	inline void init(){
		dfs1(1,1);
		dfs2(1,1);
	}
};
namespace Tree{
	int sum,cnt,top,Max,root;
	int T[N],siz[N];
	pair<int,ll> dis[N];
	bool del[N];
	inline void add(int x,int y){
		if(x>y)
		  swap(x,y);
		G[x].push_back(y);
	}
	inline void getroot(int u,int fa){
		int s=0;
		siz[u]=1;
		for(auto t:E[u]){
			ll v=t.first;
			if(del[v]||v==fa)
			  continue;
			getroot(v,u);
			siz[u]+=siz[v];
			s=max(s,siz[v]);
		}
		s=max(s,sum-siz[u]);
		if(s<Max){
			Max=s;
			root=u;
		}
	}
	inline void Get(int u,int p){
		root=0;
		sum=Max=p;
		getroot(u,0);
		getroot(root,0);
	}
	inline void getdis(int u,int fa,ll d){
		dis[++cnt]={u,d};
		for(auto t:E[u]){
			int v=t.first,w=t.second;
			if(v==fa||del[v])
			  continue;
			getdis(v,u,d+w);
		}
	}
	inline void calc(int u){
		cnt=0;
		getdis(u,0,0);
		sort(dis+1,dis+cnt+1);
		top=0;
		for(int i=1;i<=cnt;i++){
			while(top&&dis[i].second<=dis[T[top]].second){
				add(dis[i].first,dis[T[top]].first);
				top--;
			}
			T[++top]=i;
		}
		top=0;
		for(int i=cnt;i>=1;i--){
			while(top&&dis[i].second<=dis[T[top]].second){
				add(dis[i].first,dis[T[top]].first);
				top--;
			}
			T[++top]=i;			
		}
	}
	inline void solve(int u){
		calc(u);
		del[u]=1;
		for(auto t:E[u]){
			int v=t.first;
			if(del[v])
			  continue;
			Get(v,siz[v]);
			solve(root);
		}
	}	
	void work(){
		Lowbit::init();
		LCA::init();
		Get(1,n);
		solve(root);
	}
};
bool End;
int main(){
//	open("A.in","A.out");
	n=read();
	for(int u,v,w,i=1;i<n;i++){
		u=read(),v=read(),w=read();
		add(u,v,w); 
	}
	q=read();
	for(int l,r,i=1;i<=q;i++){
		l=read(),r=read();
		Q[l].push_back({i,r});
	}
	Tree::work();
	for(int i=n;i>=1;i--){
		for(auto v:G[i])
		  Lowbit::add(v,LCA::dis(i,v));
		for(auto t:Q[i])
		  ans[t.first]=Lowbit::query(t.second);
	}
	for(int i=1;i<=q;i++){
		write(ans[i]==INF?-1:ans[i]);
		putchar('\n');
	}
	cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
	return 0;
}

标签:rt,P9058,支配,le,ICPC2022,int,rpmtdq,operatorname,dis
From: https://www.cnblogs.com/rgw2010/p/18331112

相关文章

  • P9058 [Ynoi2004] rpmtdq
    前言开个巨坑,可能以后会三四天一更?分析路径考虑点分治,以下的\(dis\)皆为确定根节点的。以下点对\((i,j)\)都钦定\(dis_i\ledis_j\)注意到很多点对对答案是无用的,条件为若点对\((i,j)\)存在\(k\in[l+1,r-1]\),满足\(dis_i>=dis_k\or\dis_j\gedis_k\),则点对\((i,j)......
  • P9668 [ICPC2022 Jinan R] Torch 题解
    思路考虑使用矩阵模拟这个过程。首先,我们可以设初值为:\[\begin{bmatrix}0&1\end{bmatrix}\]表示瘦子初始走\(0\)米,胖子初始走\(1\)米。考虑瘦子走一步。由于瘦子每走一步都不能超过胖子,我们可以使用\((\min,+)\)矩乘来维护这个性质。那么瘦子走一步是:\[\begin{bma......
  • P9669 [ICPC2022 Jinan R] DFS Order 2
    P9669[ICPC2022JinanR]DFSOrder2树形dp+回退背包dfs的过程时走到\(u\),如果走进一个子树后要回到\(u\),那么这个子树一定全部遍历了一遍。所以方案数会跟子树遍历的方案数有关,可以预处理。设\(h_u\)表示\(u\)子树的遍历方案,假如\(u\)有\(m\)个儿子,那么有\(h_u=......
  • 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)\)什么时候一定没......
  • P9361 [ICPC2022 Xi'an R] Contests
    更好的阅读体验P9361[ICPC2022Xi'anR]Contests首先观察一下性质,每个\(l\)在每场比赛里一定是对应着某个前缀。发现题目要求的是最小的满足要求的\(l\),最暴力的大概是维护五个指针,每次答案\(+1\),然后尝试跳一步,什么时候某个前缀包含了\(x\)当前的计数器就是答案。考......
  • ICPC2022 Xi'an R A Bridge
    洛谷传送门QOJ传送门感觉很妙啊,应该不止蓝吧?首先一个转化是每次建桥操作就相当于交换两条链的后半部分,可以看看扶苏那篇题解的图。我们将每个点表示为形如\((x,y)\)的二元组表示它初始在第\(x\)行第\(y\)列,按\(y\)为键值排序,那么一次询问就是查询一条链的最大值。......
  • [Ynoi2004] rpmtdq 题解
    人生第一发\(Ynoi\)的题,写一篇题解庆祝一下传送门我们可以发现,对于二元组\((x,y)\),若存在一个\(dist(i,j)\ledist(x,y),x<i<j<y\)那么答案肯定不是二元组\((x,y)\)我们可以考虑把这些肯定不是的点剔除掉考虑怎么找,我们可以先点分治,求出每个点......
  • ICPC2022Hangzhou C No Bug No Game 题解
    LinkICPC2022HangzhouCNoBugNoGameQuestion给定\(n\)个物品和上限\(k\),要求最大化分数,物品的选择顺序可以任意第\(i\)个物品一行\(p_i\)代表个数,后面\(p_i\)个\(w_j\)代表容量,定义\(sum=\sum\limits_{j=1}^{i-1}\),对于第\(i\)个物品\(sum+p_i\lek\)......
  • ICPC2022Hangzhou A Modulo Ruins the Legend 题解
    LinkICPC2022HangzhouAModuloRuinstheLegendQuestion求$$\sum\limits_{i=1}^na_i+n\timess+\frac{n(n+1)}{2}\timesd\modm$$的最小值Solution我们把这个式子看成一一个二元不定方程\(ax+by+sum\modm\)的最小值,其中\(a=n,b=\frac{n(n+1)}{2},sum=\sum\limits......