【2021 HDU多校集训第四场】1011. Travel on Tree

Description

给你一颗n个点的树,边有边权。有m次询问,每次询问给出[l,r],要求编号为[l,r]之间的点组成的虚树边权和*2。

n , m ≤ 1 0 5 n,m\leq 10^5 n,m105
多组询问,最多10组极限数据,时限30s

Solution

用不带删除的莫队来做,

枚举 n \sqrt{n} n 次起点,设为S,可以预处理出 S到S+1,S到S+2…,S到n 这n-S+1棵虚树的边权大小。
具体的,我们只需要维护每次往虚树中加一个点后,虚树权值的变化量,也就是找dfn第一个比当前点大和第一个比当前点小的点,
正着加点不好维护,考虑反过来做,就变成删点,这样就可以用并查集维护第一个比当前点大和第一个比当前点小的点,

对于每个询问,找暴力将小于S的那一部分点加进虚树中,

使用RMQ-LCA可以实现 O ( 1 ) O(1) O(1)的查询LCA,
复杂度: O ( n n ) O(n\sqrt{n}) O(nn )

卡常卡了半天QwQ

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define efo(i,q) for(int i=A[q];i;i=B[i][0])
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N=100500,INF=1e9+7;
char bf[1000000], *kdp1 = bf, *kdp2 = bf;
#define nc() (kdp1==kdp2&&(kdp2=(kdp1=bf)+fread(bf,1,1000000,stdin),kdp2==kdp1)?-1:*kdp1++)
inline int read(int &x) {
    x = 0;
    char ch = nc();
    for (; ch < '0' || ch > '9'; ch = nc());
    for (; ch <= '9' && ch >= '0'; x = x * 10 + ch - 48, ch = nc());
    return x;
}
char pf[20000000],*kxo1=pf,*kxo2=pf+20000000;
#define ot(x) (kxo1==kxo2?fwrite(pf,1,20000000,stdout),*(kxo1=pf)++=x:*kxo1++=x)
inline void print(LL x){static char s[20],*b;b=s;if(!x)*b++=48;for(;x;*b++=x%10+48,x/=10);for(;b--!=s;ot(*b));}

int n,m,ans;
int B[N*2][3],A[N],B0;
vector<pii>GU[N];
LL Ans[N];
void link(int q,int w,int e)
{
	B[++B0][0]=A[q],A[q]=B0,B[B0][1]=w;B[B0][2]=e;
	B[++B0][0]=A[w],A[w]=B0,B[B0][1]=q;B[B0][2]=e;
}

int dfn[N],dzx[N],dep[N];//
LL dis[N];

pii rmq[N*2][20];
int er[22],lg2[N*2];
int rzx[N],rmq0;
void dfsf(int q,int fa,int fav)
{
	dzx[dfn[q]=++dfn[0]]=q;
	dep[q]=dep[fa]+1;
	dis[q]=dis[fa]+fav;
	
	rmq[rzx[q]=++rmq0][0]={dep[q],q};
	efo(i,q)if(B[i][1]!=fa)
	{
		dfsf(B[i][1],q,B[i][2]);
		rmq[++rmq0][0]={dep[q],q};
	}
}

struct ASKv
{
	int l,r,L,i;
}ask[N];
LL f[N];

int ff[N][2];
LL ffv[N][3];

int G[2][N];
int gf(int I,int q){return G[I][q]==q?q:G[I][q]=gf(I,G[I][q]);}
int LCA(int q,int w)
{
	int l=rzx[q],r=rzx[w];
	if(l>r)swap(l,r);
	int t=lg2[r-l+1];
	return min(rmq[l][t],rmq[r-er[t]+1][t]).second;
}
int p[N],p1[500],p2[500];
void baoli(int aS,int la)
{
	int l=ask[aS].l,r=ask[aS].r;

	p[0]=0;
	if(l>=la)
	{
		fo(j,1,p2[0])if(p2[j]>=l&&p2[j]<=r)p[++p[0]]=p2[j];
	}
	else {
		fo(j,1,p1[0])if(p1[j]>=l&&p1[j]<=r)p[++p[0]]=p1[j];
	}
	LL ans=0;
	p[p[0]+1]=p[1];
	fo(j,2,p[0]+1)
	{
		int w=LCA(p[j],p[j-1]);
		ans+=dis[p[j-1]]+dis[p[j]]-2*dis[w];
	}
	Ans[ask[aS].i]=ans;
}
void resetG(int S)
{
	fo(i,1,S-1)
	{
		int q=dfn[i];
		G[0][q]=q-1;
		G[1][q]=q+1;
	}
	fo(i,S,n)
	{
		int q=dfn[i];
		G[0][q]=G[1][q]=q;
	}
	G[0][n+1]=G[1][n+1]=n+1;
}
void init()
{
	int q,w,e;
	read(n),read(m);
	fo(i,2,n)read(q),read(w),read(e),link(q,w,e);
	int K=sqrt(n)+1;
	K=min(n,K);
	fo(i,1,m)
	{
		read(ask[i].l),read(ask[i].r);
		ask[i].i=i;
		ask[i].L=(ask[i].l-1)/K;
	}
	sort(ask+1,ask+1+m,[](ASKv q,ASKv w){return q.L<w.L||(q.L==w.L&&q.r<w.r);});

	dfsf(1,0,0);
	fo(j,1,18)
	{
		fo(i,1,rmq0-er[j-1])rmq[i][j]=min(rmq[i][j-1],rmq[i+er[j-1]][j-1]);
	}

	int STR=clock();
	int aS=1;
	for(int S=K,la=0,kn=1;1;++kn,la=S,S=min(n,S+K))
	{
		// printf("%d %d\n",S,clock()-STR);
		STR=clock();

		p1[0]=0;
		fo(i,la+1,S-1)p1[++p1[0]]=i;
		sort(p1+1,p1+1+p1[0],[](int q,int w){return dfn[q]<dfn[w];});
		p2[0]=0;
		fo(i,la+1+(K>>1),S-1)p2[++p2[0]]=i;
		sort(p2+1,p2+1+p2[0],[](int q,int w){return dfn[q]<dfn[w];});

		int mx=0;
		for(;aS<=m&& ask[aS].L==kn-1;++aS)
		{
			if(ask[aS].r<S)
			{
				baoli(aS,la+1+(K>>1));
			}else {
				GU[ask[aS].r].push_back({ask[aS].l,ask[aS].i});
				mx=max(mx,ask[aS].r);
			}
		}

		resetG(S);

		// printf("  %d\n",clock()-STR);
		
		f[S]=0;

		fod(i,n,mx+1)q=dfn[i],G[0][q]=q-1,G[1][q]=q+1;
		fod(i,mx,S)
		{
			for(auto nw:GU[i])
			{
				int l=nw.first;
				p[0]=0;
				if(l>=la+1+(K>>1))
				{
					fo(j,1,p2[0])if(p2[j]>=l)p[++p[0]]=p2[j];
				}
				else {
					fo(j,1,p1[0])if(p1[j]>=l)p[++p[0]]=p1[j];
				}
				LL ans=0;
				fo(j,1,p[0])
				{
					int l=dzx[gf(0,dfn[p[j]])];
					if(j>1&& (!l||dfn[l]<dfn[p[j-1]]))l=p[j-1];
					if(!l)l=dzx[gf(0,n)];

					int r=dzx[gf(1,dfn[p[j]])];
					if(!r)
					{
						r=dzx[gf(1,1)];
						if(j>1&& dfn[r]>dfn[p[1]])r=p[1];
					}

					ans-=dis[r]+dis[l]-2*dis[LCA(r,l)];
					ans+=dis[p[j]]+dis[l]-2*dis[LCA(p[j],l)];
					ans+=dis[p[j]]+dis[r]-2*dis[LCA(p[j],r)];
				}
				Ans[nw.second]=ans;
			}

			q=dfn[i];
			G[0][q]=q-1;
			G[1][q]=q+1;
			if(i==S)continue;

			int l,r;
			l=dzx[gf(0,q)];
			if(!l)l=dzx[gf(0,n)];
			
			r=dzx[gf(1,q)];
			if(!r)r=dzx[gf(1,1)];

			if(ff[i][0]!=l||ff[i][1]!=r)ffv[i][2]=-2*dis[LCA(l,r)];
			if(ff[i][0]!=l)ff[i][0]=l,ffv[i][0]=-2*dis[LCA(l,i)];
			if(ff[i][1]!=r)ff[i][1]=r,ffv[i][1]=-2*dis[LCA(r,i)];
			f[i]=dis[i]*2+ffv[i][0]+ffv[i][1]-ffv[i][2];

		}

		LL sum=0;
		GU[S].clear();
		fo(i,S+1,mx)
		{
			sum+=f[i];
			for(auto nw:GU[i])
			{
				Ans[nw.second]+=sum;
			}
			GU[i].clear();
		}
		if(S==n)break;
	}

	fo(i,1,m)
	{
		print(Ans[i]);
		ot('\n');
		// printf("%lld\n",Ans[i]);
	}
	
	B0=0;rmq0=0;
	fo(i,1,n)A[i]=dzx[i]=0;
	fo(i,1,n)
	{
		ff[i][0]=ff[i][1]=0;
		fo(j,0,2)ffv[i][j]=0;
	}
	dfn[0]=0;
}
int main()
{
	int STR=clock();
	int q,w,_;
	er[0]=1;fo(j,1,18)er[j]=er[j-1]<<1;
	fo(i,1,200000)lg2[i]=log2(i);
	read(_);
	while(_--)
	{
		init();
	}
	cerr<<clock()-STR<<endl;
	fwrite(pf,1,kxo1-pf,stdout);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值