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

    III.II.[SDOI2016]游戏

    明显,一条从 \(x\)\(y\) 的路径可以被拆作两条从LCA下来的路径,并且路径上每个点被写上的数是关于其深度的一次函数。

    于是就树剖套李超树就行了。

    但是有个问题,李超树不是只支持单点询问吗,怎么这里又支持区间了呢?

    我们发现,对于一条线段,其与我们询问区间可能有三种关系:相离,此时不需考虑;被完全包含于询问区间内,此时最小值一定在端点处取得,就直接用数据结构维护,插入线段时只处理两个端点,询问区间时询问所有被包含于其中的端点,无论是BIT还是常规线段树都可以轻松解决。

    而剩下一种就是该线段不完全被包含于询问区间内,但依据单调性最优点一定处于端点位,所以就直接在李超树上询问端点处的最优答案即可。

    复杂度 \(O(n\log^3n)\)。但是,李超树常数极小,配上树剖,仍然能搞过去。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll inf=123456789123456789ll;
    int n,m,head[100100],cnt;
    struct node{int to,next,val;}edge[200100];
    void ae(int u,int v,int w){
    	edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
    	edge[cnt].next=head[v],edge[cnt].to=u,edge[cnt].val=w,head[v]=cnt++;
    }
    int fa[100100],dep[100100],son[100100],sz[100100],dfn[100100],rev[100100],top[100100],tot;
    ll dis[100100];
    void dfs1(int x){
    	sz[x]=1;
    	for(int i=head[x];i!=-1;i=edge[i].next){
    		if(edge[i].to==fa[x])continue;
    		fa[edge[i].to]=x,dis[edge[i].to]=dis[x]+edge[i].val,dep[edge[i].to]=dep[x]+1;
    		dfs1(edge[i].to);
    		sz[x]+=sz[edge[i].to];
    		if(sz[edge[i].to]>sz[son[x]])son[x]=edge[i].to;
    	}
    }
    void dfs2(int x){
    	dfn[x]=++tot,rev[tot]=x;if(!top[x])top[x]=x;
    	if(son[x])top[son[x]]=top[x],dfs2(son[x]);
    	for(int i=head[x];i!=-1;i=edge[i].next)if(edge[i].to!=fa[x]&&edge[i].to!=son[x])dfs2(edge[i].to);
    }
    int LCA(int x,int y){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);x=fa[top[x]];}if(dep[x]>dep[y])swap(x,y);return x;}
    #define lson x<<1
    #define rson x<<1|1
    #define mid ((l+r)>>1)
    namespace FS{//full section
    	ll mn[400100];
    	void build(int x,int l,int r){mn[x]=inf;if(l!=r)build(lson,l,mid),build(rson,mid+1,r);}
    	void modify(int x,int l,int r,int P,ll val){if(l>P||r<P)return;mn[x]=min(mn[x],val);if(l!=r)modify(lson,l,mid,P,val),modify(rson,mid+1,r,P,val);}
    	ll query(int x,int l,int r,int L,int R){if(l>R||r<L)return inf;if(L<=l&&r<=R)return mn[x];return min(query(lson,l,mid,L,R),query(rson,mid+1,r,L,R));}
    }
    namespace BS{//boundary situation
    	struct SegTree{ll b;int k;}seg[400100];
    	void build(int x,int l,int r){seg[x].k=0,seg[x].b=inf;if(l!=r)build(lson,l,mid),build(rson,mid+1,r);}
    	void modify(int x,int l,int r,int L,int R,int K,ll B){
    		if(l>R||r<L)return;
    		if(L<=l&&r<=R){
    			if(dis[rev[mid]]*K+B<dis[rev[mid]]*seg[x].k+seg[x].b)swap(seg[x].k,K),swap(seg[x].b,B);
    			if(dis[rev[l]]*K+B<dis[rev[l]]*seg[x].k+seg[x].b)modify(lson,l,mid,L,R,K,B);
    			if(dis[rev[r]]*K+B<dis[rev[r]]*seg[x].k+seg[x].b)modify(rson,mid+1,r,L,R,K,B);
    			return;
    		}
    		modify(lson,l,mid,L,R,K,B),modify(rson,mid+1,r,L,R,K,B);
    	}
    	ll query(int x,int l,int r,int P){
    		if(l>P||r<P)return inf;
    		ll ret=dis[rev[P]]*seg[x].k+seg[x].b;
    		if(l!=r)ret=min(ret,query(lson,l,mid,P)),ret=min(ret,query(rson,mid+1,r,P));
    		return ret;
    	}
    }
    void modify(int L,int R,int K,ll B){L=dfn[L],R=dfn[R],FS::modify(1,1,n,L,dis[rev[L]]*K+B),FS::modify(1,1,n,R,dis[rev[R]]*K+B),BS::modify(1,1,n,L,R,K,B);}
    ll query(int L,int R){L=dfn[L],R=dfn[R];return min(FS::query(1,1,n,L,R),min(BS::query(1,1,n,L),BS::query(1,1,n,R)));}
    void chain(int x,int y,int K,ll B){while(top[x]!=top[y])modify(top[x],x,K,B),x=fa[top[x]];modify(y,x,K,B);}
    void pathmodify(int x,int y,int A,int B){int z=LCA(x,y);chain(x,z,-A,dis[x]*A+B),chain(y,z,A,(dis[x]-dis[z]*2)*A+B);}
    ll pathquery(int x,int y){
    	ll ret=inf;
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])swap(x,y);
    		ret=min(ret,query(top[x],x)),x=fa[top[x]];
    	}
    	if(dep[x]>dep[y])swap(x,y);ret=min(ret,query(x,y));return ret;
    }
    int main(){
    	scanf("%d%d",&n,&m),memset(head,-1,sizeof(head));
    	for(int i=1,x,y,z;i<n;i++)scanf("%d%d%d",&x,&y,&z),ae(x,y,z);
    	dfs1(1),dfs2(1);
    //	for(int x=1;x<=n;x++)printf("FA:%d SN:%d SZ:%d DP:%d DS:%lld RV:%d DF:%d TP:%d\n",fa[x],son[x],sz[x],dep[x],dis[x],rev[x],dfn[x],top[x]);
    	FS::build(1,1,n),BS::build(1,1,n);
    	for(int i=1,x,y,a,b,tp;i<=m;i++){
    		scanf("%d%d%d",&tp,&x,&y);
    		if(tp==1)scanf("%d%d",&a,&b),pathmodify(x,y,a,b);
    		else printf("%lld\n",pathquery(x,y));
    	}
    	return 0;
    }
    

  • 相关阅读:
    js键盘按钮keyCode及示例大全
    HTML 禁止复制文字
    【Layui】formSelects下拉多选框取值
    JQuery 判断访问的浏览器是pc还是手机
    php发送post请求的方法
    用PHP实现小写金额转换大写金额【精确到分】
    PHP获取文件大小的几种方法!
    js jquery 数组的合并 对象的合并
    web页面长时间未操作自动退出登录
    获取公众号素材管理列表-教程
  • 原文地址:https://www.cnblogs.com/Troverld/p/14620731.html
Copyright © 2011-2022 走看看