首页 > 其他分享 >luogu P4775 [NOI2018] 情报中心

luogu P4775 [NOI2018] 情报中心

时间:2022-12-24 17:33:09浏览次数:67  
标签:P4775 int luogu Mx && Line NOI2018 id ER

题面传送门

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

首先题目要求的是选出两条路径\((x_1,y_1),(x_2,y_2)\)满足有交,并且并的部分减去\(w_1+w_2\)最大。

不妨假设\(p_1\)是\(x_1\)与\(x_2\)的LCA,并且是路径交离\(x_1\)和\(x_2\)最近的一点,\(p_2\)是\(y_1\)和\(y_2\)的LCA,并且是路径交离\(x_1\)和\(x_2\)最近的一点,则这个权值的两倍等于\(dist(x_1,y_1)+dist(x_2,y_2)+dist(x_1,x_2)+dist(y_1,y_2)-2w_1-2w_2\)。

考虑枚举\(p_1\),然后这个权值变为\(dist(x_1,y_1)+dist(x_2,y_2)+d_{x_1}+d_{x_2}-2d_{p_1}+dist(y_1,y_2)-2w_1-2w_2\)。

将\(dist(x_1,y_1)+d_{x_1}-2w_1\)看成\(y_1\)的点权,同样的东西看成\(y_2\)的点权,那么就是\(y_1\)到\(y_2\)的路径长度加上点权减去当前的\(2d_{p_1}\)。

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

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

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();
}

标签:P4775,int,luogu,Mx,&&,Line,NOI2018,id,ER
From: https://www.cnblogs.com/275307894a/p/17003076.html

相关文章

  • luogu
    1973(DP,双指针)注意:题目中的区间\((l,r)\)是开区间!\(pre(i,j)\)表示前\(i\)个位置,某个地点选了\(j\)个活动,另一个地点所能选的活动数量的最大值。\(suf(i,j)\)表......
  • luoguP5383 普通多项式转下降幂多项式 题解
    学习了bztMinamoto大佬的做法,希望这篇题解可以使得那个方法更加易于理解。既然下降幂多项式转普通多项式可以采取分治\(\operatorname{NTT}\),那么可以猜测逆过来也可以......
  • luoguP2254 [NOI2005]瑰丽华尔兹 题解
    传送门题意给定\(n\)行\(m\)列的矩阵和钢琴的初始位置\((x,y)\),给定\(k\)个时间段,在每个时间段内钢琴会向给定方向(上/下/左/右)滑动,\(1\)秒移动\(1\)个单位长度......
  • Luogu4194 / LOJ115 - 网络流 -
    题目链接:https://www.luogu.com.cn/problem/P4194题解:LOJ115是无源汇上下界可行流的板子题Luogu4194需要一定建模无源汇上下界可行流,需要求一张图的流函数,使得满足流......
  • [Ynoi2018]天降之物
    題目描述:你需要实现\(m\)个操作,操作有两种:\(1\).把序列中所有值为\(x\)的数的值变成\(y\)。\(2\).找出一个位置\(i\)满足$a_{i}=x\(,找出一个位置\)j$满足\(a_{j}=y\),使......
  • Luogu 省选计划 #1
    整体二分CDQ分治问题2(P3527)一次模拟下雨是把所有国家的一起下了,不妨所有国家一起二分。二分定义域为时间轴,初始把所有国家都挂在\(k/2\)上,然后根据结果分别缩小......
  • luogu省选计划day1
    T1过水已隐藏T2整体二分经典题,主席树上二分也可以在二分时要算一个国家的每一个出现位置,看起来是不行的但是均摊一下没有问题使用线段树辅助计算答案T3过水已隐藏T......
  • [数学记录]Luogu4284 概率充电器
    NOIp前随机一道题来补。NOIp2022rp++题意:给定一棵树,每个点\(p_i\)概率被直接激活,每条边\(p_{\{u,v\}}\)概率被激活,被激活的点可以顺着被激活的边激活其它点,求所......
  • luogu P4465 [国家集训队] JZPSTR
    题面传送门我真的,我哭死,如果考了我当场感谢zyq。听说std是SAM+块链,我瑟瑟发抖,然后祭出了bitset大法。据说这个是叫一种Shift-And的基于位运算的字符串匹配算法,也就是说,......
  • luogu1253 [yLOI2018] 扶苏的问题 题解
    扶苏的问题题目题目描述给定一个长度为\(n\)的序列\(a\),要求支持如下三个操作:给定区间\([l,r]\),将区间内每个数都修改为\(x\)。给定区间\([l,r]\),将区间内每......