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

    HINT

     n≤100000,m≤100000,∣a∣≤10000,0<=w,|b|<=10^9

     
    首先树链剖分一下,那么我们只要解决序列的问题就能用乘一个logn的代价解决树上问题了。
    我们要支持的两个操作是:
    1.在[l,r]区间内添加一条y=kx+b的直线。
    2.询问[l,r]区间内所有直线的最小值。
    我们考虑使用线段树来维护这些直线,考虑用懒标记在每个节点上标记一条覆盖整个区间的直线。
    如果一个节点上已经有一条直线,我们又在上面加上了一条直线,我们应该怎么维护这两条直线的并呢?
    计算出两条直线左右端点的值,如果存在一个直线整体在另一条直线上的情况,直接保留最优直线即可。
    否则计算两条直线在端点出的函数值,在这个节点保留最优区间长的一条直线,把另一条直线向下传递即可。
    对于每个修改操作,[l,r]区间可以拆成logn个节点,每个节点因为每次长度至少减半,所以最多传递logn次,时间复杂度为O(log^2n)。
    对于每个询问操作,直接在每个走过的直线和拆成的区间上查询即可,时间复杂度为O(logn)。
    然后树上问题随便讨论讨论就行了。
    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    typedef long long ll;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
    	if(head==tail) {
    		int l=fread(buffer,1,BufferSize,stdin);
    		tail=(head=buffer)+l;
    	}
    	return *head++;
    }
    inline int read() {
        int x=0,f=1;char c=Getchar();
        for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
        return x*f;
    }
    const int maxn=100010;
    const ll inf=123456789123456789ll;
    ll ans,minv[maxn<<2],val[maxn],dist[maxn];
    int n,m,first[maxn],next[maxn<<1],to[maxn<<1],dis[maxn<<1],e;
    void AddEdge(int w,int v,int u) {
    	to[++e]=v;dis[e]=w;next[e]=first[u];first[u]=e;
    	to[++e]=u;dis[e]=w;next[e]=first[v];first[v]=e;
    }
    int fa[maxn],dep[maxn],son[maxn],siz[maxn];
    void dfs(int x) {
    	dep[x]=dep[fa[x]]+1;siz[x]=1;
    	ren if(to[i]!=fa[x]) {
    		fa[to[i]]=x;dist[to[i]]=dist[x]+dis[i];
    		dfs(to[i]);siz[x]+=siz[to[i]];
    		if(siz[to[i]]>siz[to[son[x]]]) son[x]=i;
    	}
    }
    int top[maxn],pos[maxn],cnt;
    void build(int x,int tp) {
    	top[x]=tp;pos[x]=++cnt;val[cnt]=dist[x];
    	if(son[x]) build(to[son[x]],tp);
    	ren if(to[i]!=fa[x]&&i!=son[x]) build(to[i],to[i]);
    }
    int setv[maxn<<2];
    struct Line {
    	int k;ll b;
    	ll f(ll x) {return x*k+b;}
    }A[maxn<<2];
    void query(int o,int l,int r,int ql,int qr) {
    	if(setv[o]) ans=min(ans,min(A[o].f(val[max(ql,l)]),A[o].f(val[min(qr,r)])));
    	if(ql<=l&&r<=qr) ans=min(ans,minv[o]);
    	else {
    		int mid=l+r>>1,lc=o<<1,rc=lc|1;
    		if(ql<=mid) query(lc,l,mid,ql,qr);
    		if(qr>mid) query(rc,mid+1,r,ql,qr);
    	}
    }
    void maintain(int o,int l,int r) {
    	int lc=o<<1,rc=lc|1;
    	if(l<r) minv[o]=min(minv[lc],minv[rc]);
    	if(setv[o]) minv[o]=min(minv[o],min(A[o].f(val[l]),A[o].f(val[r])));
    }
    void cover(int o,int l,int r,Line x) {
    	if(!setv[o]) setv[o]=1,A[o]=x;
    	else if(l==r) {
    		if(A[o].f(val[l])>x.f(val[l])) A[o]=x;
    	}
    	else {
    		int mid=l+r>>1,lc=o<<1,rc=lc|1;
    		ll fl=A[o].f(val[l]),fr=A[o].f(val[r]);
    		ll gl=x.f(val[l]),gr=x.f(val[r]);
    		if(fl>=gl&&fr>=gr) A[o]=x;
    		else if(fl<=gl&&fr<=gr) return;
    		else {
    			if(gl<fl) {
    				if(A[o].f(val[mid])<=x.f(val[mid])) cover(lc,l,mid,x);
    				else cover(rc,mid+1,r,A[o]),A[o]=x;
    			}
    			if(gr<fr) {
    				if(A[o].f(val[mid+1])<=x.f(val[mid+1])) cover(rc,mid+1,r,x);
    				else cover(lc,l,mid,A[o]),A[o]=x;
    			}
    		}
    	}
    	maintain(o,l,r);
    }
    void update(int o,int l,int r,int ql,int qr,Line x) {
    	if(ql<=l&&r<=qr) cover(o,l,r,x);
    	else {
    		int mid=l+r>>1,lc=o<<1,rc=lc|1;
    		if(ql<=mid) update(lc,l,mid,ql,qr,x);
    		if(qr>mid) update(rc,mid+1,r,ql,qr,x);
    		maintain(o,l,r);
    	}
    }
    int query(int x,int y,int tp) {
    	int f1=top[x],f2=top[y];
    	while(f1!=f2) {
    		if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
    		if(tp) query(1,1,n,pos[f1],pos[x]);
    		x=fa[f1];f1=top[x];
    	}
    	if(dep[x]>dep[y]) swap(x,y);
    	if(tp) query(1,1,n,pos[x],pos[y]);
    	return x;
    }
    void update(int x,int z,int k,ll b) {
    	int f=top[x];
    	while(f!=top[z]) {
    		update(1,1,n,pos[f],pos[x],(Line){k,b});
    		x=fa[f];f=top[x];
    	}
    	update(1,1,n,pos[z],pos[x],(Line){k,b});
    }
    int main() {
    	n=read();m=read();
    	rep(i,2,n) AddEdge(read(),read(),read());
    	dfs(1);build(1,1);
    	rep(i,1,n*4) minv[i]=inf;
    	rep(i,1,m) {
    		int tp=read(),x=read(),y=read();
    		if(tp==2) {
    			ans=inf;query(x,y,1);
    			printf("%lld
    ",ans);
    		}
    		else {
    			int a=read(),b=read();
    			int z=query(x,y,0);
    			update(x,z,-a,b+dist[x]*a);
    			update(y,z,a,b+(dist[x]-2*dist[z])*a);
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    构造 非构造 代码块
    Random 类生成随机数
    JAVA寄存器
    PyCharm配置远程python解释器和在本地修改服务器代码
    Java实现常见的排序算法
    推荐系统冷启动问题解决方案
    AVL树C代码
    AVL树->图解2
    AVL树->图解1
    二叉查找树(Binary Sort Tree)
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/5483072.html
Copyright © 2011-2022 走看看