P4775 [NOI2018] 情报中心

首先至少需要有一条公共边。

感觉存在一个想法就是考虑枚举这个公共边,然后考虑在这个边上统计答案。但是存在问题就是可能重合的边不止一条,这样的话我们就不能很好地统计贡献了。。。

我们可以将一次收集情报的贡献看成两条路径的权值和减去公共部分的权值和再减去两条路径的自带权值。

这就启发我们有一个 \(m^2\) 的暴力,但是这个暴力是不能拓展的。

目前有一个类似于树上差分的想法,将路径放到其上的每一条边上,这样我们枚举到每一条边的时候,就可以获得过当前边的路径集合,关键是我们该如何利用这个集合?


这道题目有没有可能是边分治?

如果考虑边分的话,我们就要考虑提取出过当前边的路径,然后将路径拆成两段。

然后问题就转化成了有若干段由根出发的路径,问两条路径在两边的树中的部分的和最大是多少。

我靠,暴力写挂?考虑贡献的式子是这样的:

\[dep_x+dep_y+dep'_x+dep'_y-(dep_{\text{lca}(x,y)}+dep'_{\text{lca}(x,y)}) \]

估计是可以用类暴力写挂的做法来做的吧。

我有一个边分治套点分治套……的做法,可惜这里太小写不下。


考虑两条路径的并还有一种计算公式:

\[\text{sum}((u_1,v_1)\cup(u_2,v_2))=\frac{\text{sum}((u_1,v_1))+\text{sum}((u_2,v_2))+\text{sum}((u_1,v_2))+\text{sum}((u_2,v_1))}{2} \]

哇哦,但是知道了这个式子我好像也不会。。。

考虑枚举两点 \(u_1,u_2\) 两点的 \(\text{lca}\) ,我们令其名为 \(t\)

那么贡献的两倍我们就可以转化为:

\[(dep_{u_1}+dis(u_1,v_1)-2v_1)+(dpe_{u_2}+dis(u_2,v_2)-2v_2)+dis(v_1,v_2)-2dep_t \]

其中 \(dep_u+dis(u,v)-2v\) 这个模块的值对于一条路径是固定的,我们令其名为 \(w\) ,即:

\[w_1+w_2+dis(v_1,v_2)-2dep_t \]

我们要使得这个答案最大,发现贡献实际上就是树上带权最远点对问题,虽然带有点权(且可能为负),但是依旧可以满足一个结论:

\[\text{diam}(S,T)\subseteq \text{diam}(S,T)\cup \text{diam}(T,T) \]

然后我们就使用这个结论来维护两棵子树合并之时的新贡献。

然后我们利用一波树上差分将路径放到其应该在的节点上即可,这里需要满足删除操作,所以用线段树维护就可以支持删除操作了。

但是有一个需要注意的点在于我们需要满足两条路径必须要存在至少一条公共边,而考虑我们上面的操作,我们只关注到了 \(\text{lca}\) 的问题,并没有考虑到至少一条公共边的问题。

我们考虑什么时候会出现存在公共点但没有公共边的情况,就是其中一条路径穿过了另一条的 \(\text{lca}\) ,所以我们在添加树上差分的时候要在 \(\text{lca}\) 处将该路径删除。

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5,M=1e5+5;
const long long INF=1e18+7;
char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read(){
	char c=getchar();int x=0;
	while(c<'0'||'9'<c) c=getchar();
	while('0'<=c&&c<='9') x=x*10+c-'0',c=getchar();
	return x;
}
inline long long llread(){
	char c=getchar();long long x=0;
	while(c<'0'||'9'<c) c=getchar();
	while('0'<=c&&c<='9') x=x*10+c-'0',c=getchar();
	return x;
}
int n,m;
struct Edge{int nxt,to,val;}e[N<<1];int fir[N];
struct edge{int id,u,v;long long w;}a[M];
void add(int u,int v,int w,int i){e[i]=(Edge){fir[u],v,w},fir[u]=i;}
struct Node{int dep,mp;long long dis;}tr[N];
int fa[16][N],st[17][N<<1],lg[N<<1],cnt_dfn=0;
inline void dfs(int u){
	st[0][++cnt_dfn]=u,tr[u].mp=cnt_dfn;
	tr[u].dep=tr[fa[0][u]].dep+1;
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa[0][u]) continue;
		tr[v].dis=tr[u].dis+e[i].val;
		fa[0][v]=u,dfs(v),st[0][++cnt_dfn]=u;
	}
}
inline void work(){
	for(int j=1;j<16;++j){
		for(int i=1;i<=n;++i)
		fa[j][i]=fa[j-1][fa[j-1][i]];
	}
	for(int j=1;j<17;++j){
		for(int i=1;i+(1<<j)-1<=cnt_dfn;++i){
			if(tr[st[j-1][i]].dep<tr[st[j-1][i+(1<<(j-1))]].dep)
			st[j][i]=st[j-1][i];else st[j][i]=st[j-1][i+(1<<(j-1))];
		}
	}
}
inline int lca(int u,int v){
	int L=tr[u].mp,R=tr[v].mp;if(L>R) swap(L,R);
	int tmp=lg[R-L+1];
	if(tr[st[tmp][L]].dep<tr[st[tmp][R-(1<<tmp)+1]].dep)
	return st[tmp][L];else return st[tmp][R-(1<<tmp)+1];
}
inline int get(int u,int v){
	for(int j=15;j>=0;--j)
		if(tr[fa[j][v]].dep>tr[u].dep) v=fa[j][v];
	return v;
}
inline long long dis(int u,int v){
	return tr[u].dis+tr[v].dis-2*tr[lca(u,v)].dis;
}
struct Data{int u,v;long long valu,valv;};
inline long long cal(Data a){
	if(!a.u||!a.v) return -INF+(a.u||a.v);
	return dis(a.u,a.v)+a.valu+a.valv;
}
bool operator < (Data a,Data b){
	return cal(a)<cal(b);
}
Data operator + (Data a,Data b){
	Data res1=(Data){a.u,b.u,a.valu,b.valu};
	Data res2=(Data){a.u,b.v,a.valu,b.valv};
	Data res3=(Data){a.v,b.u,a.valv,b.valu};
	Data res4=(Data){a.v,b.v,a.valv,b.valv};
	return max({res1,res2,res3,res4});
}
struct Seg_Tree{
	int rt[N],tot;
	struct Node{int ls,rs;Data f;}tr[M*35];
	inline void up(int u){
		tr[u].f=tr[tr[u].ls].f+tr[tr[u].rs].f;
		tr[u].f=max({tr[u].f,tr[tr[u].ls].f,tr[tr[u].rs].f});
	}
	inline void clear(){
		for(int i=1;i<=tot;++i)
			tr[i].ls=tr[i].rs=0,tr[i].f=(Data){0,0,0,0};
		tot=0;
	}
	inline void modify(int u,int l,int r,int x,Data z){
		if(l==r) return tr[u].f=z,void();
		int mid=(l+r)>>1;
		if(x<=mid){
			if(!tr[u].ls) tr[u].ls=++tot;
			return modify(tr[u].ls,l,mid,x,z),up(u);
		}
		else{
			if(!tr[u].rs) tr[u].rs=++tot;
			return modify(tr[u].rs,mid+1,r,x,z),up(u);
		}
	}
	inline int merge(int u,int v,int l,int r){
		if(!u||!v) return u|v;
		int mid=(l+r)>>1;
		tr[u].ls=merge(tr[u].ls,tr[v].ls,1,mid);
		tr[u].rs=merge(tr[u].rs,tr[v].rs,mid+1,r);
		return up(u),u;
	}
}t;
vector<edge> bag[N],BAG[N];
long long res;
inline void DFS(int u,int fa){
	t.rt[u]=++t.tot;
	for(int i=0;i<(int)bag[u].size();++i){
		edge e=bag[u][i];
		long long w=tr[e.u].dis+dis(e.u,e.v)-2*e.w;
		Data f=(Data){e.v,0,w,0};
		res=max(res,cal(f+t.tr[t.rt[u]].f)-2*tr[u].dis);
		t.modify(t.rt[u],1,m,e.id,f);
	}
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa) continue;
		DFS(v,u);Data f=t.tr[t.rt[u]].f+t.tr[t.rt[v]].f;
		res=max(res,cal(f)-2*tr[u].dis);
		t.rt[u]=t.merge(t.rt[u],t.rt[v],1,m);
	}
	for(int i=0;i<(int)BAG[u].size();++i){
		edge e=BAG[u][i];
		t.modify(t.rt[u],1,m,e.id,(Data){0,0,0,0});
	}
}
inline int solve(){
	n=read();
	for(int i=1;i<=n;++i) fir[i]=0;
	for(int i=1;i<n;++i){
		int u=read(),v=read(),w=read();
		add(u,v,w,i<<1),add(v,u,w,i<<1|1);
	}
	cnt_dfn=0,dfs(1),work();
	m=read();
	for(int i=1;i<=n;++i) bag[i].clear(),BAG[i].clear();
	for(int i=1;i<=m;++i){
		a[i].u=read(),a[i].v=read(),a[i].w=llread();
		if(a[i].u==a[i].v) continue;
		int tmp=lca(a[i].u,a[i].v);a[i].id=i;
		if(tmp==a[i].v) swap(a[i].u,a[i].v);
		if(tmp==a[i].u){
			bag[a[i].v].push_back((edge){a[i].id,a[i].v,a[i].u,a[i].w});
			BAG[get(tmp,a[i].v)].push_back(a[i]);
		}
		else{
			bag[a[i].u].push_back(a[i]);
			bag[a[i].v].push_back((edge){a[i].id,a[i].v,a[i].u,a[i].w});
			BAG[get(tmp,a[i].u)].push_back(a[i]);
			BAG[get(tmp,a[i].v)].push_back((edge){a[i].id,a[i].v,a[i].u,a[i].w});
		}
	}
	res=-INF,t.clear(),DFS(1,0);
	if(res<=-INF+1) return printf("F\n"),0;
	else return printf("%lld\n",res/2),0;
}
int main(){
	// freopen("data.in","r",stdin);
	// freopen("shit.out","w",stdout);
	lg[1]=0;for(int i=2;i<(N<<1);++i) lg[i]=lg[i>>1]+1;
	int T=read();while(T--) solve();
	return 0;
}
posted @ 2021-12-16 22:44  Point_King  阅读(63)  评论(0编辑  收藏  举报