6837. 【2020.10.31提高组模拟】小y的数论

一棵树,带边权。若干次询问:询问编号\([l,r]\)内的点中,选出\(k\)个点,建出的虚树的边权和最大是多少。

\(n,m\le 3*10^5\)


结论题+码农题。

原题题解中解释,但是题解中冒出若干个题目没有出现过的字母,以及很长的形式化的式子,也不加解释,感觉就是出题人在自嗨。总之就是看不懂。

首先,如何求\([1,n]\)的答案。有个结论是:\(k\)的最优解一定包含\(k-1\)的最优解。那么每次找到没有选过的最长链。可以发现这其实可以以直径端点为根长链剖分,选前\(k-1\)长的链。

另一个结论:设\(p_{S,k}\)表示\(k\)为多少时,\(S\)的最优解。那么一定有\(p_{S\bigcup T,k}\subseteq p_{S,k}\bigcup p_{T,k}\)。

于是求\(p_{S\bigcup T,k}\)时,可以以\(p_{S,k}\bigcup p_{T,k}\)中的点建虚树,然后套用上面的方法来求出最优解。

因此\(p_{S,k}\)是可以维护的。以\(K\)(\(k\)的最大值)分块,对于\(\frac{n}{K}\)个块建ST表即可,查询的时候用ST表查整块暴力查散块,合并起来求最优解即可。

时间复杂度\(O(n\lg n+qK\lg K)\)。后面\(\lg k\)其实可以省掉但是没有必要因为省掉之后代码量增大并且常数也大。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <vector>
#include <cassert>
#include <cstdlib>
#define N 300005
#define maxK 100
#define ll long long
int n;
struct EDGE{
	int to;
	ll w;
	EDGE *las;
};
struct Graph{
	EDGE e[N*2];
	int ne;
	EDGE *last[N];
	void link(int u,int v,ll w){
//		printf("%d %d %lld\n",u,v,w);
		e[ne]={v,w,last[u]};
		last[u]=e+ne++;
	}
} G,F;
int fa[N],dep[N],siz[N],hs[N],top[N],dfn[N],nowdfn;
ll sum[N];
void init1(int x){
	siz[x]=1;
	for (EDGE *ei=G.last[x];ei;ei=ei->las)
		if (ei->to!=fa[x]){
			fa[ei->to]=x;
			dep[ei->to]=dep[x]+1;
			sum[ei->to]=sum[x]+ei->w;
			init1(ei->to);
			siz[x]+=siz[ei->to];
			if (siz[ei->to]>siz[hs[x]])
				hs[x]=ei->to;
		}
}
void init2(int x,int t){
	top[x]=t;
	dfn[x]=++nowdfn;
	if (hs[x]){
		init2(hs[x],t);
		for (EDGE *ei=G.last[x];ei;ei=ei->las)
			if (ei->to!=fa[x] && ei->to!=hs[x])
				init2(ei->to,ei->to);
	}
}
int LCA(int u,int v){
	for (;top[u]!=top[v];u=fa[top[u]])
		if (dep[top[u]]<dep[top[v]])
			swap(u,v);
	return dep[u]<dep[v]?u:v;
}

int K,bel[N],L[N],R[N],nb;
vector<int> f[N/maxK+5][19];
bool cmpdfn(int x,int y){return dfn[x]<dfn[y];}
int g[N];
int build(vector<int> &q,vector<int> &p){
//	printf("-----\n");
//	printf("\n");
//	for (int i=1;i<q.size();++i)
//		if (dfn[q[i-1]]>=dfn[q[i]])
//			printf("fuck\n");
	assert(q.size()>=1);
	static int st[N],tp;
	p.clear();
	st[tp=1]=q[0];	
	for (int i=1;i<q.size();++i){
		int lca=LCA(st[tp],q[i]);
		while (tp>1 && dep[lca]<dep[st[tp-1]]){
			F.link(st[tp-1],st[tp],sum[st[tp]]-sum[st[tp-1]]);
			F.link(st[tp],st[tp-1],sum[st[tp]]-sum[st[tp-1]]);
			g[st[tp]]=st[tp-1];
			p.push_back(st[tp--]);
		}
		if (tp && dep[lca]<dep[st[tp]]){
			F.link(lca,st[tp],sum[st[tp]]-sum[lca]);
			F.link(st[tp],lca,sum[st[tp]]-sum[lca]);
			g[st[tp]]=lca;
			p.push_back(st[tp--]);
			if (st[tp]!=lca)
				st[++tp]=lca;
		}
		st[++tp]=q[i];
	}
	while (tp>1){
		F.link(st[tp-1],st[tp],sum[st[tp]]-sum[st[tp-1]]);
		F.link(st[tp],st[tp-1],sum[st[tp]]-sum[st[tp-1]]);
		g[st[tp]]=st[tp-1];
		p.push_back(st[tp--]);
	}
	p.push_back(st[1]);
//	printf("------\n");
	return st[1];
}
ll dis[N];
void getdis(int x,int fa){
//	if (fa!=g[x])
//		printf("%d\n",x);
	for (EDGE *ei=F.last[x];ei;ei=ei->las)
		if (ei->to!=fa){
			if (dis[ei->to]==0){
//				printf("%d\n",ei->to);
				assert(dis[ei->to]==0);
			}
			dis[ei->to]=dis[x]+ei->w;
			getdis(ei->to,x);
		}
}
ll len[N];
int bot[N];
vector<int> t;
bool cmpt(int x,int y){return len[x]>len[y];}
void work(int x,int fa){
	len[x]=0;
	bot[x]=x;
	for (EDGE *ei=F.last[x];ei;ei=ei->las)
		if (ei->to!=fa){
			work(ei->to,x);
			len[ei->to]+=ei->w;
			if (len[ei->to]>len[x])
				len[x]=len[ei->to],bot[x]=bot[ei->to];
		}
	for (EDGE *ei=F.last[x];ei;ei=ei->las)
		if (ei->to!=fa && bot[ei->to]!=bot[x])
			t.push_back(ei->to);
}
void merge(vector<int> &c,vector<int> &a,vector<int> &b){
	static vector<int> q;
	q.clear();
	int i=0,j=0;
	while (i<a.size() && j<b.size())
		if (dfn[a[i]]<dfn[b[j]])
			q.push_back(a[i++]);
		else if (dfn[a[i]]>dfn[b[j]])
			q.push_back(b[j++]);
		else
			q.push_back(a[i++]),j++;
	for (;i<a.size();++i)
		q.push_back(a[i]);
	for (;j<b.size();++j)
		q.push_back(b[j]);
	c=q;
}
ll calc(vector<int> &q,int k=K){
	static vector<int> p;
	int rt=build(q,p);
	dis[rt]=0,getdis(rt,0);
//	exit(0);
	for (int i=0;i<p.size();++i)
		if (dis[p[i]]>dis[rt])
			rt=p[i];
	t.clear(),work(rt,0),t.push_back(rt);
	q.clear(),q.push_back(rt);
	ll res=0;
	if (p.size()>1){
		if (t.size()>k-1)
			sort(t.begin(),t.end(),cmpt);
		for (int i=0;i<t.size() && i<k-1;++i){
			q.push_back(bot[t[i]]);
			res+=len[t[i]];
		}
	}
	sort(q.begin(),q.end(),cmpdfn);
	F.ne=0;
	for (int i=0;i<p.size();++i)
		F.last[p[i]]=NULL;
	return res;
}
int lg[N];
ll query(int l,int r,int k){
	static vector<int> q,p;
	if (bel[l]==bel[r]){
		p.clear();
		for (int i=l;i<=r;++i)
			p.push_back(i);
		sort(p.begin(),p.end(),cmpdfn);
		return calc(p,k);
	}
	int Lb=bel[l]+1,Rb=bel[r]-1;
	if (Lb<=Rb){
		int m=lg[Rb-Lb+1];
		merge(q,f[Lb][m],f[Rb-(1<<m)+1][m]);
	}
	else
		q.clear();
	p.clear();
	for (int i=l;i<=R[bel[l]];++i)
		p.push_back(i);
	for (int i=r;i>=L[bel[r]];--i)
		p.push_back(i);
	sort(p.begin(),p.end(),cmpdfn);
	merge(q,q,p);
	return calc(q,k);
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	freopen("number.in","r",stdin);
	freopen("number.out","w",stdout);	 	 
	scanf("%d",&n);
	lg[0]=-1,lg[1]=0;
	for (int i=2;i<=n;++i)
		lg[i]=lg[i>>1]+1;
	for (int i=1;i<n;++i){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		G.link(u,v,w),G.link(v,u,w);
	}
	init1(1),init2(1,1);
	K=min(n,maxK);
	for (int i=1;i<=n;++i)
		bel[i]=(i-1)/K+1,R[bel[i]]=i,nb=bel[i];
	for (int i=n;i>=1;--i)
		L[bel[i]]=i;
	for (int i=1;i<=nb;++i){
		for (int j=L[i];j<=R[i];++j)
			f[i][0].push_back(j);
		sort(f[i][0].begin(),f[i][0].end(),cmpdfn);
	}
	for (int j=1;1<<j<=nb;++j){
		for (int i=1;i+(1<<j)-1<=nb;++i){
			merge(f[i][j],f[i][j-1],f[i+(1<<j-1)][j-1]);
			calc(f[i][j]);	
//			sort(f[i][j].begin(),f[i][j].end(),cmpdfn);
		}
	}
	int Q;
	scanf("%d",&Q);
	int cnt=0;
	while (Q--){
		int l,r,k;
		scanf("%d%d%d",&l,&r,&k);
		printf("%lld\n",query(l,r,k));
	}
	return 0;
}
上一篇:RK算法


下一篇:简述EI电离源的工作原理