把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P4775 [NOI2018] 情报中心

题面传送门

牛逼题,第一步转化都想不到。

首先题目要求的是选出两条路径(x1,y1),(x2,y2)满足有交,并且并的部分减去w1+w2最大。

不妨假设p1x1x2的LCA,并且是路径交离x1x2最近的一点,p2y1y2的LCA,并且是路径交离x1x2最近的一点,则这个权值的两倍等于dist(x1,y1)+dist(x2,y2)+dist(x1,x2)+dist(y1,y2)2w12w2

考虑枚举p1,然后这个权值变为dist(x1,y1)+dist(x2,y2)+dx1+dx22dp1+dist(y1,y2)2w12w2

dist(x1,y1)+dx12w1看成y1的点权,同样的东西看成y2的点权,那么就是y1y2的路径长度加上点权减去当前的2dp1

维护这种最远点对显然可以只维护直径两个点,然后用欧拉序的LCA就可以做到O(1)合并。于是可以用线段树合并维护这个过程,在[x1,LCA(x1,y1))放下y1这个点,在另一端上放下x1这个点。记得删除就好了。合并的时候也可以计算答案。

时间复杂度O(mlogm),真的很卡常,特别是一条链。

code:

#include<bits/stdc++.h>
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n))
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=5e4+5,M=1e7+5,K=1e6+5,mod=998244353,Mod=mod-1;const ll INF=1e18+7;const db eps=1e-5;mt19937 rnd(263082);
struct IO{
    static const int S=1<<21;
    char buf[S],*p1,*p2;int st[105],Top;
    ~IO(){clear();}
    inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
    inline void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
    inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    inline IO&operator >> (char&x){while(x=gc(),x==' '||x=='\n'||x=='\r');return *this;}
    template<typename T>inline IO&operator >> (T&x){
        x=0;bool f=0;char ch=gc();
        while(ch<'0'||ch>'9'){if(ch=='-') f^=1;ch=gc();}
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=gc();
        f?x=-x:0;return *this;
    }
    inline IO&operator << (const char c){pc(c);return *this;}
    template<typename T>inline IO&operator << (T x){
        if(x<0) pc('-'),x=-x;
        do{st[++st[0]]=x%10,x/=10;}while(x);
        while(st[0]) pc('0'+st[st[0]--]);return *this;
    }
}fin,fout;
int n,m,k,x,y,Rt[N];ll W[N],z,Ans;
struct Edge{int to,w;};vector<Edge> S[N];
namespace ER{
	int fa[N][20],lg[N*2],Id[20][N*2],Bg[N],Df[N*2],Dh;unsigned short d[N];
	void Make(int x,int La){Me(fa[x],0);
		Df[Bg[x]=++Dh]=x;d[x]=d[La]+1;fa[x][0]=La;for(int i=1;fa[x][i-1];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
		for(Edge i:S[x]) i.to^La&&(W[i.to]=W[x]+i.w,Make(i.to,x),Df[++Dh]=x);
	}
	void BD(){Dh=0;
		int i,j;Make(1,0);for(i=2;i<=Dh;i++) lg[i]=lg[i/2]+1;
		for(i=Dh;i;i--) for(Id[0][i]=Df[i],j=1;i+(1<<j)-1<=Dh;j++) Id[j][i]=(d[Id[j-1][i]]<d[Id[j-1][i+(1<<j-1)]]?Id[j-1][i]:Id[j-1][i+(1<<j-1)]);
	}
	int LCA(int x,int y){x=Bg[x];y=Bg[y];x>y&&(swap(x,y),0);int D=lg[y-x+1];return d[Id[D][x]]<d[Id[D][y-(1<<D)+1]]?Id[D][x]:Id[D][y-(1<<D)+1];}
	ll Dt(int x,int y){return W[x]+W[y]-2*W[LCA(x,y)];}
	int Jump(int x,int y){for(int i=lg[d[x]];~i;i--) d[fa[x][i]]>d[y]&&(x=fa[x][i]);return x;}
}
struct Node{int id;ll w;}C1;
struct Line{
	Node A,B;ll W;
	Line operator +(const Line &C)const{ll D[6],Mx;
		if(!A.id&&!B.id) return C;if(!C.A.id&&!C.B.id) return (Line){A,B,W};
		if(A.id&&B.id)D[0]=W;else D[0]=-INF;if(A.id&&C.A.id)D[1]=A.w+C.A.w+ER::Dt(A.id,C.A.id);else D[1]=-INF;
		if(A.id&&C.B.id)D[2]=A.w+C.B.w+ER::Dt(A.id,C.B.id);else D[2]=-INF;if(B.id&&C.A.id)D[3]=B.w+C.A.w+ER::Dt(B.id,C.A.id);else D[3]=-INF;
		if(B.id&&C.B.id) D[4]=B.w+C.B.w+ER::Dt(B.id,C.B.id);else D[4]=-INF;if(C.A.id&&C.B.id)D[5]=C.W;else D[5]=-INF;
		Mx=max(max(D[0],D[1]),max(max(D[2],D[3]),max(D[4],D[5])));
		if(D[0]==Mx) return (Line){A,B,W};if(D[1]==Mx) return (Line){A,C.A,D[1]};if(D[2]==Mx) return (Line){A,C.B,D[2]};
		if(D[3]==Mx) return (Line){B,C.A,D[3]};if(D[4]==Mx) return (Line){B,C.B,D[4]};return C; 
	}
	ll operator *(const Line &C)const{
		ll Mx=-INF;if(A.id&&C.A.id) Mx=max(Mx,A.w+C.A.w+ER::Dt(A.id,C.A.id));
		if(A.id&&C.B.id) Mx=max(Mx,A.w+C.B.w+ER::Dt(A.id,C.B.id));
		if(B.id&&C.A.id) Mx=max(Mx,B.w+C.A.w+ER::Dt(B.id,C.A.id));
		if(B.id&&C.B.id) Mx=max(Mx,B.w+C.B.w+ER::Dt(B.id,C.B.id));return Mx;
	} 
}C2;
namespace Tree{
	Line f[M];int L[M],R[M],Ct;void Up(int v){f[v]=f[L[v]]+f[R[v]];}
	void Cl(){for(int i=1;i<=Ct;i++) L[i]=R[i]=0,f[i]=C2;Ct=0;}
	void Ins(int x,Line y,int &v,int l=1,int r=m){!v&&(v=++Ct);f[v]=f[v]+y;if(l==r)return;int Mi=l+r>>1;x<=Mi?Ins(x,y,L[v],l,Mi):Ins(x,y,R[v],Mi+1,r);}
	void Del(int x,int &v,int l=1,int r=m){if(l==r) {v=0;return;}int Mi=l+r>>1;x<=Mi?Del(x,L[v],l,Mi):Del(x,R[v],Mi+1,r);Up(v);}
	int Merge(int x,int y){if(!x||!y) return x|y;f[x]=f[x]+f[y];L[x]=Merge(L[x],L[y]);R[x]=Merge(R[x],R[y]);return x;}
}
struct Ques{int p;Node B;};vector<Ques> T[N];vector<int> D[N]; 
void GA(int x,int La){
	Rt[x]=0;for(Ques i:T[x]) {
		Line p=(Line){i.B,C1};Ans=max(Ans,p*Tree::f[Rt[x]]-2*W[x]);//cerr<<Ans<<' '<<x<<'\n';
		Tree::Ins(i.p,p,Rt[x]);
	}for(Edge i:S[x]) {
		GA(i.to,x);Ans=max(Ans,Tree::f[Rt[x]]*Tree::f[Rt[i.to]]-2*W[x]);//cerr<<Ans<<' '<<x<<'\n';
		Rt[x]=Tree::Merge(Rt[x],Rt[i.to]);
	}
	for(int i:D[x]) Tree::Del(i,Rt[x]);
}
void Solve(){
	int i,j;for(i=1;i<=n;i++) S[i].clear(),T[i].clear(),D[i].clear();fin>>n;for(i=1;i<n;i++) fin>>x>>y>>z,S[x].PB((Edge){y,z});
	ER::BD();
	Tree::Cl();fin>>m;for(i=1;i<=m;i++){
		fin>>x>>y>>z;int Ls=ER::LCA(x,y);ll Len=W[x]+W[y]-2*W[Ls];
		if(x^Ls) T[x].PB((Ques){i,(Node){y,Len+W[x]-2*z}}),D[ER::Jump(x,Ls)].PB(i);
		if(y^Ls) T[y].PB((Ques){i,(Node){x,Len+W[y]-2*z}}),D[ER::Jump(y,Ls)].PB(i);
	}Ans=-5e18;GA(1,0);if(Ans>-1e18)printf("%lld\n",Ans/2);else puts("F"); 
}
int main(){
	freopen("1.in","r",stdin);freopen("1.out","w",stdout);
	int t;scanf("%d",&t);while(t--) Solve();
}
posted @   275307894a  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2021-12-24 luogu P4007 小 Y 和恐怖的奴隶主
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示