P9678 [ICPC2022 Jinan R] Tree Distance

发布时间 2024-01-03 21:46:34作者: WrongAnswer_90

更好的阅读体验

P9678 [ICPC2022 Jinan R] Tree Distance

支配对,不是非常难。

显然如果 \(a\leq b<c\leq d\)\(dis(a,d)>dis(b,c)\) 则点对 \((a,d)\) 是无用的,猜想点对数不会太多,事实也正是如此。

树上距离是很复杂的东西,考虑简化:使 \(root\) 为根,暂且让 \(dis(x,y)=dep_x+dep_y\)(正确性后面会证)。此时一个点对 \((x,y)\) 是有用的的充要条件是不存在 \(x\leq a<b\leq y\)\(dep_a+dep_b<dep_x+dep_y\)

这个条件仍然不好刻画,观察一下性质可以发现充要条件可以变成:\(dep_x,dep_y\) 是区间 \([x,y]\)\(dep\) 的最小和次小值,充分性和必要性都显然。这样钦定一个根最多会有 \(\mathcal O(siz_x)\) 个点对。

一个推论是支配点对一定不会出现相交。所以可以类似分治的思想:把点按照编号排序,此时能和最左边的点匹配的点一定是类似单调栈一样的东西,可以记录 \(nx_i\) 表示编号比 \(i\) 大且 \(dep\)\(i\) 小的最小的点,这样暴力跳,因为一定不会出现相交,分治的时候递归 \((now,nx_{now})\) 即可。

上面还有一个问题:我们是强制让 \(dis(x,y)=dep_x+dep_y\) 的,但是实际距离可能并不是。但是这个并不会影响任何东西。首先如果 \((x,y)\) 作为支配点对出现,那不会影响任何东西(常熟可能会大一些)。如果通过 \(a\leq x<y\geq b\)\((x,y)\)\((a,b)\) 叉掉了,那如果 \(dis(a,b)\) 变得更小一定更能叉掉。所以上面讨论的实际上是一个必要的点对,但是复杂度是对的。剩下的东西就是淀粉质板子了。最后扫描线树状数组维护后缀 \(\min\),复杂度 \(\mathcal O(n\log^2n+q\log n)\)

	int n,m,cnt,len,ans[1000010],head[200010],v[400010],to[400010],nex[400010];
	inline void add(int x,int y,int z){v[++cnt]=z,to[cnt]=y,nex[cnt]=head[x],head[x]=cnt;}
	vector<pii> ve[200010],qu[200010];
	namespace BIT
	{
		int t[200010];
		inline void add(int x,int y){for(;x;x-=x&-x)Mmin(t[x],y);}
		inline int ask(int x){int s=INF;for(;x<=n;x+=x&-x)Mmin(s,t[x]);return s;}
	}
	using namespace BIT;
	namespace Starch
	{
		int nx[200010],id[200010],siz[200010],dep[200010],a[200010],len,root,all;
		bool vis[200010];
		set<int> st;
		void find(int x,int fa)
		{
			siz[x]=1;int maxn=0;
			for(int i=head[x];i;i=nex[i])
			{
				if(to[i]==fa||vis[to[i]])continue;
				find(to[i],x),siz[x]+=siz[to[i]],Mmax(maxn,siz[to[i]]);
			}
			if(max(maxn,all-siz[x])<=all/2)root=x;
		}
		void dfs(int x,int fa)
		{
			a[++len]=x;
			for(int i=head[x];i;i=nex[i])
			{
				if(to[i]==fa||vis[to[i]])continue;
				dep[to[i]]=dep[x]+v[i],dfs(to[i],x);
			}
		}
		void calc(int l,int r)
		{
			int now=l+1;
			while(now&&now<=r)
			{
				ve[a[now]].eb(mp(a[l],dep[a[l]]+dep[a[now]]));
				if(dep[a[now]]<dep[a[l]]||!nx[now])return calc(now,r);
				calc(now,min(r,nx[now])),now=nx[now];
			}
		}
		inline bool cmp(int x,int y){return dep[a[x]]<dep[a[y]]||(dep[a[x]]==dep[a[y]]&&x>y);}
		void starch(int x)
		{
			find(x,0),find(x=root,0),vis[x]=1,dep[x]=len=0,dfs(x,0),sort(a+1,a+1+len);
			for(int i=1;i<=len;++i)id[i]=i;
			sort(id+1,id+1+len,cmp),st.clear();
			for(int i=1;i<=len;++i)
			{
				auto it=st.lower_bound(id[i]);
				if(it==st.end())nx[id[i]]=0;else nx[id[i]]=*it;
				st.insert(id[i]);
			}
			calc(1,len);
			for(int i=head[x];i;i=nex[i])if(!vis[to[i]])all=siz[to[i]],starch(to[i]);
		}
	}
	using namespace Starch;
	inline void mian()
	{
		read(n),all=n;int x,y,z;
		for(int i=1;i<=n;++i)t[i]=INF;
		for(int i=1;i<n;++i)read(x,y,z),add(x,y,z),add(y,x,z);
		read(m),starch(1);
		for(int i=1;i<=m;++i)read(x,y),qu[y].eb(mp(x,i));
		for(int i=1;i<=n;++i)
		{
			for(auto j:ve[i])add(j.fi,j.se);
			for(auto j:qu[i])ans[j.se]=ask(j.fi);
		}
		for(int i=1;i<=m;++i)write(ans[i]==INF?-1:ans[i],'\n');
	}