一棵树,带边权。若干次询问:询问编号([l,r])内的点中,选出(k)个点,建出的虚树的边权和最大是多少。
(n,mle 3*10^5)
结论题+码农题。
原题题解中解释,但是题解中冒出若干个题目没有出现过的字母,以及很长的形式化的式子,也不加解释,感觉就是出题人在自嗨。总之就是看不懂。
首先,如何求([1,n])的答案。有个结论是:(k)的最优解一定包含(k-1)的最优解。那么每次找到没有选过的最长链。可以发现这其实可以以直径端点为根长链剖分,选前(k-1)长的链。
另一个结论:设(p_{S,k})表示(k)为多少时,(S)的最优解。那么一定有(p_{Sigcup T,k}subseteq p_{S,k}igcup p_{T,k})。
于是求(p_{Sigcup T,k})时,可以以(p_{S,k}igcup p_{T,k})中的点建虚树,然后套用上面的方法来求出最优解。
因此(p_{S,k})是可以维护的。以(K)((k)的最大值)分块,对于(frac{n}{K})个块建ST表即可,查询的时候用ST表查整块暴力查散块,合并起来求最优解即可。
时间复杂度(O(nlg n+qKlg 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
",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("-----
");
// printf("
");
// for (int i=1;i<q.size();++i)
// if (dfn[q[i-1]]>=dfn[q[i]])
// printf("fuck
");
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("------
");
return st[1];
}
ll dis[N];
void getdis(int x,int fa){
// if (fa!=g[x])
// printf("%d
",x);
for (EDGE *ei=F.last[x];ei;ei=ei->las)
if (ei->to!=fa){
if (dis[ei->to]==0){
// printf("%d
",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
",query(l,r,k));
}
return 0;
}