http://hihocoder.com/problemset/problem/1381
一个结论:集合A的直径为a--b,集合B的直径为c--d,那么集合A∪B的直径必是以下6种之一:
a--b c--d a--c a--d b--c b--d
断掉一条边,相当于从树的dfs序上取出一段区间
用线段树维护dfs序上任意一段区间的直径
如果[1,10]断掉的是[1,4] [3,4] [7,8]
答案就是[1,2]的直径+[3,4]的直径+[5,6]∪[9,10]的直径+[7,8]的直径
#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 100001
typedef long long LL;
int n;
int tot,front[N],nxt[N<<1],to[N<<1],val[N<<1];
int lo2[N];
int dep[N],dy[N],fa[N][18];
LL dis[N];
int ll[N],rr[N];
vector<int>inc[N];
int st[N],top;
int a[N],bin[N],cnt;
LL ans;
struct node
{
int a,b;
LL dis;
}tr[N<<2];
void read(int &x)
{
x=0; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}
void add(int u,int v,int w)
{
to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; val[tot]=w;
}
void init()
{
read(n);
int u,v,w;
for(int i=1;i<n;++i)
{
read(u); read(v); read(w);
add(u,v,w);
}
}
void dfs(int x)
{
ll[x]=++tot;
dy[tot]=x;
int t;
for(int i=front[x];i;i=nxt[i])
{
t=to[i];
if(fa[t][0]) continue;
fa[t][0]=x;
dis[t]=dis[x]+val[i];
dep[t]=dep[x]+1;
dfs(t);
}
rr[x]=tot;
}
void pre()
{
for(int i=2;i<=n;++i) lo2[i]=lo2[i>>1]+1;
fa[1][0]=-1;
tot=0;
dfs(1);
int m=lo2[n];
fa[1][0]=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
fa[i][j]=fa[fa[i][j-1]][j-1];
}
int getlca(int x,int y)
{
int m=lo2[dep[x]];
for(int i=m;i>=0;--i)
if(ll[fa[x][i]]>ll[y]) x=fa[x][i];
return fa[x][0];
}
LL getdis(int x,int y)
{
x=dy[x];
y=dy[y];
if(x==y) return 0;
if(ll[x]<ll[y]) swap(x,y);
int lca=getlca(x,y);
if(lca==y) return dis[x]-dis[y];
return dis[x]+dis[y]-dis[lca]*2;
}
node unionn(node p,node q)
{
node t1=(node){p.a,q.a,getdis(p.a,q.a)};
node t2=(node){p.a,q.b,getdis(p.a,q.b)};
node t3=(node){p.b,q.a,getdis(p.b,q.a)};
node t4=(node){p.b,q.b,getdis(p.b,q.b)};
node t=p;
if(q.dis>t.dis) t=q;
if(t1.dis>t.dis) t=t1;
if(t2.dis>t.dis) t=t2;
if(t3.dis>t.dis) t=t3;
if(t4.dis>t.dis) t=t4;
return t;
}
void build(int k,int l,int r)
{
if(l==r)
{
tr[k].a=tr[k].b=l;
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
tr[k]=unionn(tr[k<<1],tr[k<<1|1]);
}
bool cmp(int p,int q)
{
return ll[p]<ll[q];
}
node query(int k,int l,int r,int opl,int opr)
{
if(l>=opl && r<=opr) return tr[k];
int mid=l+r>>1;
if(opr<=mid) return query(k<<1,l,mid,opl,opr);
if(opl>mid) return query(k<<1|1,mid+1,r,opl,opr);
node tmp1=query(k<<1,l,mid,opl,opr);
node tmp2=query(k<<1|1,mid+1,r,opl,opr);
return unionn(tmp1,tmp2);
}
void dfs2(int x)
{
int l=ll[x],m=inc[x].size(),t;
node tmp,mx;
mx.a=mx.b=ll[x];
mx.dis=0;
for(int i=0;i<m;++i)
{
t=inc[x][i];
if(ll[t]!=l)
{
tmp=query(1,1,n,l,ll[t]-1);
mx=unionn(mx,tmp);
}
dfs2(t);
l=rr[t]+1;
}
if(l!=rr[x]+1)
{
tmp=query(1,1,n,l,rr[x]);
mx=unionn(mx,tmp);
}
ans+=mx.dis;
}
void solve()
{
int m,k,x,y;
read(m);
while(m--)
{
for(int i=1;i<=cnt;++i) inc[bin[i]].clear();
cnt=ans=0;
st[top=1]=1;
read(k);
for(int i=1;i<=k;++i)
{
read(x);
x<<=1;
if(ll[to[x]]<ll[to[x-1]]) y=to[x-1];
else y=to[x];
a[i]=y;
}
sort(a+1,a+k+1,cmp);
for(int i=1;i<=k;++i)
{
y=a[i];
while(!(ll[y]>=ll[st[top]] && rr[y]<=rr[st[top]])) top--;
inc[st[top]].push_back(y);
bin[++cnt]=st[top];
st[++top]=y;
}
dfs2(1);
cout<<ans<<'\n';
}
}
int main()
{
init();
pre();
build(1,1,n);
solve();
}
时间限制:24000ms
单点时限:4000ms
内存限制:512MB
描述
小Y有一棵n个节点的树,每条边都有正的边权。
小J有q个询问,每次小J会删掉这个树中的k条边,这棵树被分成k+1个连通块。小J想知道每个连通块中最远点对距离的和。
这里的询问是互相独立的,即每次都是在小Y的原树上进行操作。
输入
第一行一个整数n,接下来n-1行每行三个整数u,v,w,其中第i行表示第i条边边权为wi,连接了ui,vi两点。
接下来一行一个整数q,表示有q组询问。
对于每组询问,第一行一个正整数k,接下来一行k个不同的1到n-1之间的整数,表示删除的边的编号。
1<=n,q,Σk<=105, 1<=w<=109
输出
共q行,每行一个整数表示询问的答案。
- 样例输入
-
5 1 2 2 2 3 3 2 4 4 4 5 2 3 4 1 2 3 4 1 4 2 2 3
- 样例输出
-
0 7 4