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

luogu P4069 [SDOI2016]游戏

题面传送门
出题人总是喜欢让李超上树(
这个东西显然树剖+线段树。
我们将修改拆成两端,那么上行那一段的直线方程就是\(y=-ad_x+d_ux+b\)
下行同理,那么直接维护即可,时间复杂度\(O(nlog^3n)\),但是那个东西常数很小。
code:

#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 100000
#define M 50000
#define mod 1000000000
#define mod2 39989
#define eps (1e-7)
#define U unsigned int
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
using namespace std;
int n,m,k,siz[N+5],top[N+5],fa[N+5],son[N+5],id[N+5],dis[N+5],idea,op,x,y,z,lcas,now,a,b;ll d[N+5],dfn[N+5];const ll Maxn=5e18;
struct yyy{int to,w,z;};
struct ljb{int head,h[N+5];yyy f[N+5<<1];I void add(int x,int y,int z){f[++head]=(yyy){y,z,h[x]},h[x]=head;}}s;
struct line{ll k,b;I ll calc(ll x){return k*x+b;}};
struct LineTree{
	line F[N+5<<4];ll Sum[N+5<<4];
	I void Up(int l,int r,int now){Sum[now]=min(min(Sum[now<<1],Sum[now<<1|1]),min(F[now].calc(dfn[l]),F[now].calc(dfn[r])));}
	I void add(line z,int l,int r,int now){
		int m=l+r>>1;if(F[now].calc(dfn[l])>z.calc(dfn[l])&&F[now].calc(dfn[r])>z.calc(dfn[r])) F[now]=z;
		else if(F[now].calc(dfn[l])<=z.calc(dfn[l])&&F[now].calc(dfn[r])<=z.calc(dfn[r]))return; 
		else if(F[now].calc(dfn[l])>=z.calc(dfn[l]))F[now].calc(dfn[m])>=z.calc(dfn[m])?(add(F[now],m+1,r,now<<1|1),F[now]=z):(add(z,l,m,now<<1),z);
		else if(F[now].calc(dfn[r])>=z.calc(dfn[r]))F[now].calc(dfn[m])>=z.calc(dfn[m])?(add(F[now],l,m,now<<1),F[now]=z):(add(z,m+1,r,now<<1|1),z);
		/*if(l^r)*/Up(l,r,now);//printf("%d %d %d %lld %lld %lld %lld\n",l,r,now,F[now].k,F[now].b,Sum[now],dfn[l]);
	}
	I void insert(int x,int y,line z,int l=1,int r=n,int now=1){
		if(x<=l&&r<=y)return add(z,l,r,now);int m=l+r>>1;x<=m&&(insert(x,y,z,l,m,now<<1),0);y>m&&(insert(x,y,z,m+1,r,now<<1|1),0);Up(l,r,now);
	}
	I ll find(int x,int y,int l=1,int r=n,int now=1){
		if(x<=l&&r<=y) return  Sum[now];int m=l+r>>1;ll ans,pus1,pus2;ans=min(F[now].calc(dfn[max(l,x)]),F[now].calc(dfn[min(y,r)])),pus1=(x<=m?find(x,y,l,m,now<<1):Maxn),pus2=(y>m?find(x,y,m+1,r,now<<1|1):Maxn);
		/*printf("%d %d %d %lld %lld %lld %lld %lld\n",l,r,now,F[now].k,F[now].b,ans,pus1,pus2);*/return min(ans,min(pus1,pus2));
	}
}S;
I void dfs1(int x,int last){
	yyy tmp;fa[x]=last;siz[x]=1;dis[x]=dis[last]+1;for(int i=s.h[x];i;i=tmp.z) tmp=s.f[i],tmp.to^last&&(d[tmp.to]=d[x]+tmp.w,dfs1(tmp.to,x),siz[x]+=siz[tmp.to],siz[son[x]]<siz[tmp.to]&&(son[x]=tmp.to));
}
I void dfs2(int x,int last){
	yyy tmp;top[x]=last;id[x]=++idea;dfn[idea]=d[x];if(!son[x]) return;dfs2(son[x],last);for(int i=s.h[x];i;i=tmp.z) tmp=s.f[i],tmp.to^fa[x]&&tmp.to^son[x]&&(dfs2(tmp.to,tmp.to),0); 
}
I void swap(int &x,int &y){x^=y^=x^=y;}
I int lca(int x,int y){while(top[x]^top[y]) dis[top[x]]>dis[top[y]]?(x=fa[top[x]]):(y=fa[top[y]]);return dis[x]<dis[y]?x:y;}
I void get(int x,int y,ll a,ll b){
	lcas=lca(x,y);/*printf("%d\n",lcas);*/now=x;while(top[x]^top[lcas])S.insert(id[top[x]],id[x],(line){-a,a*d[now]+b}),x=fa[top[x]];S.insert(id[lcas],id[x],(line){-a,a*d[now]+b});
	while(top[y]^top[lcas])S.insert(id[top[y]],id[y],(line){a,a*(d[now]-2*d[lcas])+b}),y=fa[top[y]];S.insert(id[lcas],id[y],(line){a,a*(d[now]-2*d[lcas])+b});
}
I  ll find(int x,int y){
	ll ans=123456789123456789,now;while(top[x]^top[y]) dis[top[x]]<dis[top[y]]&&(swap(x,y),0),now=S.find(id[top[x]],id[x]),ans=min(ans,now),x=fa[top[x]];
	dis[x]>dis[y]&&(swap(x,y),0);now=S.find(id[x],id[y]);return min(ans,now); 
}
signed main(){
	freopen("1.in","r",stdin);freopen("1.out","w",stdout);
	re int i;scanf("%d%d",&n,&m);for(i=1;i<=(n<<3);i++) S.Sum[i]=Maxn,S.F[i].b=Maxn;for(i=1;i<n;i++) scanf("%d%d%d",&x,&y,&z),s.add(x,y,z),s.add(y,x,z);
	dfs1(1,0);dfs2(1,1);while(m--){
		scanf("%d",&op);if(op==1)scanf("%d%d%d%d",&x,&y,&a,&b),get(x,y,a,b);
		else scanf("%d%d",&x,&y),printf("%lld\n",find(x,y));
	}
}
posted @ 2021-07-14 21:33  275307894a  阅读(39)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end