zoukankan      html  css  js  c++  java
  • [bzoj4515] [SDOI2016]游戏

    Description

    Alice 和 Bob 在玩一个游戏。

    游戏在一棵有 n 个点的树上进行。最初,每个点上都只有一个数字,那个数字是 123456789123456789。

    有时,Alice 会选择一条从 s 到 t 的路径,在这条路径上的每一个点上都添加一个数字。对于路径上的一个点 r,

    若 r 与 s 的距离是 dis,那么 Alice 在点 r 上添加的数字是 a×dis+b。有时,Bob 会选择一条从 s 到 t 的路径。

    他需要先从这条路径上选择一个点,再从那个点上选择一个数字。

    Bob 选择的数字越小越好,但大量的数字让 Bob 眼花缭乱。Bob 需要你帮他找出他能够选择的最小的数字。

    Input

    第一行两个数字 n、m,表示树的点数和进行的操作数。

    接下来 n−1 行,每行三个数字 u、v、w,表示树上有一条连接 u、v 的边,长度是 w。

    接下来 m 行。每行第一个数字是 1 或 2。

    若第一个数是 1,表示 Alice 进行操作,接下来四个数字 s、t、a、b。

    若第一个数是 2,表示 Bob 进行操作,接下来四个数字 s、t。

    Output

    每当 Bob 进行操作,输出一行一个数,表示他能够选择的最小的数字

    Sample Input

    3 5
    1 2 10
    2 3 20
    2 1 3
    1 2 3 5 6
    2 2 3
    1 2 3 -5 -6
    2 2 3
    

    Sample Output

    123456789123456789
    6
    -106
    

    Solution

    对于一次修改((s,t,a,b)),可以分为两部分:

    • (s)(lca),对于链上的(x),新加的值为(acdot(dis[s]-dis[x])+b),其中(dis[x])(1)(x)的距离。可以发现这玩意是一个关于(dis[x])的一次函数,设函数为(y=kx+b)的话,那么(k=-a,b=a cdot dis[s]+b),所以可以用一个树剖加李超线段树来维护,李超线段树模板题——[HEOI2013]Segment
    • (t)(lca),同样的,值为(acdot (dis[x]+dis[s]-2cdot dis[lca])+b),和上面本质相同。
    #include<bits/stdc++.h>
    using namespace std;
    
    #define int long long 
    
    void read(int &x) {
        x=0;int f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
        for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
    }
     
    void print(int x) {
        if(x<0) putchar('-'),x=-x;
        if(!x) return ;print(x/10),putchar(x%10+48);
    }
    void write(int x) {if(!x) putchar('0');else print(x);putchar('
    ');}
    
    const int maxn = 3e5+10;
    const int inf = 123456789123456789;
    
    int n,m,dis[maxn],re[maxn];
    
    struct data {
    	int l,r,k,b;
    	int calc(int x) {return k*x+b;}
    };
    
    #define ls p<<1
    #define rs p<<1|1
    #define mid ((l+r)>>1)
    
    struct Segment_Tree {
    	data t[maxn<<2];
    	int mn[maxn<<2];
    
    	void update(int p) {mn[p]=min(mn[p],min(mn[ls],mn[rs]));}
    
    	void cover(int p,int l,int r,data s) {
    		int lt=t[p].calc(dis[re[l]]),rt=t[p].calc(dis[re[r]]);
    		int lp=s.calc(dis[re[l]]),rp=s.calc(dis[re[r]]);
    		if(lp>=lt&&rp>=rt) return ;
    		if(lp<=lt&&rp<=rt) {t[p]=s;mn[p]=min(min(lp,rp),mn[p]);return ;}
    		double po=1.0*(t[p].b-s.b)/(1.0*(s.k-t[p].k));
    		if(lp>lt) {
    			if(po<=(double)dis[re[mid]]) cover(ls,l,mid,t[p]),t[p]=s;
    			else cover(rs,mid+1,r,s);
    		} else {
    			if(po<=(double)dis[re[mid]]) cover(ls,l,mid,s);
    			else cover(rs,mid+1,r,t[p]),t[p]=s;
    		}
    		mn[p]=min(min(lp,rp),mn[p]),update(p);
    	}
    		
    	void modify(int p,int l,int r,data s) {
    		if(s.l<=l&&r<=s.r) return cover(p,l,r,s),void();
    		if(s.l<=mid) modify(ls,l,mid,s);
    		if(s.r>mid) modify(rs,mid+1,r,s);
    		update(p);
    	}
    	
    	int query(int p,int l,int r,int x,int y) {
    		if(x<=l&&r<=y) return mn[p];
    		int ans=min(t[p].calc(dis[re[max(x,l)]]),t[p].calc(dis[re[min(r,y)]]));
    		if(t[p].b==inf) ans=inf;
    		if(x<=mid) ans=min(ans,query(ls,l,mid,x,y));
    		if(y>mid) ans=min(ans,query(rs,mid+1,r,x,y));
    		return ans;
    	}
    	
    	void build(int p,int l,int r) {
    		t[p]=(data){l,r,0,inf};mn[p]=inf;
    		if(l==r) return ;
    		build(ls,l,mid),build(rs,mid+1,r);
    	}
    }SGT;
    
    struct Heavy_Light_Decomposation {
    	int head[maxn],tot,dfn[maxn],hs[maxn],top[maxn],sz[maxn],dfn_cnt,dep[maxn],f[maxn];
    	struct edge{int to,nxt,w;}e[maxn<<1];
    
    	void add(int u,int v,int w) {e[++tot]=(edge){v,head[u],w},head[u]=tot;}
    	void ins(int u,int v,int w) {add(u,v,w),add(v,u,w);}
    
    	void dfs(int x,int fa) {
    		f[x]=fa,dep[x]=dep[fa]+1,sz[x]=1;
    		for(int i=head[x];i;i=e[i].nxt)
    			if(e[i].to!=fa) {
    				dis[e[i].to]=dis[x]+e[i].w;
    				dfs(e[i].to,x);
    				sz[x]+=sz[e[i].to];
    				if(sz[hs[x]]<sz[e[i].to]) hs[x]=e[i].to;
    			}
    	}
    
    	void dfs2(int x) {
    		if(hs[f[x]]==x) top[x]=top[f[x]];
    		else top[x]=x;
    		dfn[x]=++dfn_cnt;re[dfn_cnt]=x;
    		if(hs[x]) dfs2(hs[x]);
    		for(int i=head[x];i;i=e[i].nxt)
    			if(e[i].to!=f[x]&&e[i].to!=hs[x]) dfs2(e[i].to);
    	}
    
    	int lca(int x,int y) {
    		while(top[x]!=top[y]) {
    			if(dep[top[x]]<dep[top[y]]) swap(x,y);
    			x=f[top[x]];
    		}if(dep[x]>dep[y]) swap(x,y);return x;
    	}
    
    	int query(int x,int y) {
    		int ans=inf;
    		while(top[x]!=top[y]) {
    			if(dep[top[x]]<dep[top[y]]) swap(x,y);
    			ans=min(ans,SGT.query(1,1,n,dfn[top[x]],dfn[x]));
    			x=f[top[x]];
    		}if(dep[x]>dep[y]) swap(x,y);
    		ans=min(ans,SGT.query(1,1,n,dfn[x],dfn[y]));
    		return ans;
    	}
    
    	void modify(int x,int t,int k,int b) {
    		while(top[x]!=top[t]) {
    			SGT.modify(1,1,n,(data){dfn[top[x]],dfn[x],k,b});
    			x=f[top[x]];
    		}SGT.modify(1,1,n,(data){dfn[t],dfn[x],k,b});
    	}
    
    	// y= a*dis+b 
    	void solve(int s,int t,int a,int b) {
    		int l=lca(s,t);
    		// y=a*dis+b=a*(dis[s]-dis[x])+b -> k=-a,b=a*dis[s]+b
    		modify(s,l,-a,a*dis[s]+b);
    	    // y=a*dis+b=a*(dis[x]+dis[s]-2*dis[l])+b -> k=a,b=a*(dis[s]-2*dis[l])+b
    		modify(t,l,a,a*(dis[s]-2*dis[l])+b);
    	}
    	
    }HLD;
    
    signed main() {
    	read(n),read(m);
    	for(int i=1,x,y,z;i<n;i++) read(x),read(y),read(z),HLD.ins(x,y,z);
    	HLD.dfs(1,0),HLD.dfs2(1),SGT.build(1,1,n);
    	for(int i=1;i<=m;i++) {
    		int op,s,t,a,b;
    		read(op),read(s),read(t);
    		if(op==1) read(a),read(b),HLD.solve(s,t,a,b);
    		else write(HLD.query(s,t));
    	}
    	return 0;
    }
    
  • 相关阅读:
    用最简单的例子理解装饰器模式(Decorator Pattern)
    用最简单的例子理解迭代器模式(Iterator Pattern)
    用最简单的例子理解观察者模式(Observer Pattern)
    用最简单的例子理解单例模式(Singleton Pattern)
    用最简单的例子理解对象为Null模式(Null Object Pattern)
    用最简单的例子理解策略模式(Strategy Pattern)
    使用Lazy<T>实现对客户订单的延迟加载
    C#关键字var是什么,在何种情况下使用
    C#中的IDisposable模式
    CLR基础,CLR运行过程,使用dos命令创建、编译、运行C#文件,查看IL代码
  • 原文地址:https://www.cnblogs.com/hbyer/p/10251833.html
Copyright © 2011-2022 走看看