[树链剖分+李超线段树] BZOJ4515: [Sdoi2016]游戏

先进行转化,把路径分成两条链, a(depsdepi)+b=(a)depi+adeps+b …类似这样变形一下,就转化成和 kdepi+b 的形式。 depi 是递增的,所以可以看做是一次函数 y=kx+b 在某些离散的点上有定义。
现在需要实现区间插入线段,求区间最小值。这个问题可以用一个称为超哥线段树的技巧解决。
大概就是线段树节点记一条线段作为标记,标记如何合并呢?不太好直接合并。
其实可以算一下两个线段的优势区间,当前区间保留长的,短的在对应儿子上继续打标记。这样打标记是 O(logn) 的。
可以标记永久化,即不下传标记,减小常数。询问时需要用和询问区间有交的区间都更新一下答案。
总复杂度 O(nlog3n)

#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
    char ch=gc(); int res=0,ff=1;
    while(!isdigit(ch)) ch=='-'?ff=-1:0, ch=gc();
    while(isdigit(ch)) res=(res<<3)+(res<<1)+ch-'0', ch=gc();
    return res*ff;
}
const int maxn=100005,maxe=200005;
const LL INF=4e18;
int n,Q,fir[maxn],nxt[maxe],son[maxe],w[maxe],tot;
int sz[maxn],hvy[maxn],pre[maxn],top[maxn],pos[maxn],df[maxn];
LL dep[maxn];
void add(int x,int y,int z){
    son[++tot]=y; w[tot]=z; nxt[tot]=fir[x]; fir[x]=tot;
}
struct node{
    LL fk,fb,_min; node* ch[2];
    node(node* son=NULL){ fk=fb=_min=INF; ch[0]=ch[1]=son; }
    inline void maintain(int L,int R){    
        if(L<R) _min=min(_min,min(ch[0]->_min,ch[1]->_min));  
        if(fk<INF) _min=min(_min,min(fb,fk*(dep[df[R]]-dep[df[L]])+fb));  
    }
} *root, nil, *null=&nil;
typedef node* P_node;
P_node Build(int L,int R){
    P_node p=new node(null); if(L==R) return p;
    int mid=(L+R)>>1;
    p->ch[0]=Build(L,mid); p->ch[1]=Build(mid+1,R);
    return p;
}
void Push_tag(P_node p,int L,int R,LL k,LL b){
    //printf("[%d,%d] %lld %lld\n",L,R,k,b);
    if(p->_min==INF){ p->fk=k; p->fb=b; p->maintain(L,R); return; }
    int mid=(L+R)>>1; LL lenL=dep[df[mid+1]]-dep[df[L]]; LL &fk=p->fk, &fb=p->fb;
    if(fk==k||L==R) fk=k, fb=min(fb,b); else
    if(k>fk&&b>fb) ; else
    if(k<fk&&b<fb) fk=k, fb=b; else 
    if(k>fk&&b<=fb){
        LL d=(fb-b)/(k-fk);
        if(d<lenL) Push_tag(p->ch[0],L,mid,k,b);
             else swap(fk,k), swap(fb,b), Push_tag(p->ch[1],mid+1,R,k,b+k*lenL);
    } else
    if(k<fk&&b>=fb){
        LL d=(fb-b)/(k-fk)+1;
        if(d>=lenL) Push_tag(p->ch[1],mid+1,R,k,b+k*lenL);
               else swap(fk,k), swap(fb,b), Push_tag(p->ch[0],L,mid,k,b);
    }
    p->maintain(L,R);
}
void Updata(P_node p,int L,int R,int qL,int qR,LL k,LL b){
    if(R<qL||qR<L) return;
    if(qL<=L&&R<=qR){ Push_tag(p,L,R,k,b+k*(dep[df[L]]-dep[df[qL]])); return; }
    int mid=(L+R)>>1;
    Updata(p->ch[0],L,mid,qL,qR,k,b); Updata(p->ch[1],mid+1,R,qL,qR,k,b);
    p->maintain(L,R);
}
LL _ORG=123456789123456789LL;
LL Query(P_node p,int L,int R,int qL,int qR){
    if(R<qL||qR<L) return _ORG;
    LL res=_ORG;
    if(p->fk<INF){
        int t1=max(L,qL),t2=min(R,qR);
        res=min(p->fk*(dep[df[t1]]-dep[df[L]])+p->fb,p->fk*(dep[df[t2]]-dep[df[L]])+p->fb); 
    }
    if(qL<=L&&R<=qR) return min(res,p->_min);
    int mid=(L+R)>>1;
    return min(res,min(Query(p->ch[0],L,mid,qL,qR),Query(p->ch[1],mid+1,R,qL,qR))); 
}
void dfs_info(int x){
    sz[x]=1;
    for(int j=fir[x];j;j=nxt[j]) if(son[j]!=pre[x]){
        dep[son[j]]=dep[x]+w[j]; pre[son[j]]=x;
        dfs_info(son[j]); sz[x]+=sz[son[j]];
        if(sz[hvy[x]]<sz[son[j]]) hvy[x]=son[j];    
    }
}
void build_chain(int x,int tp){
    df[++df[0]]=x; pos[x]=df[0]; top[x]=tp;
    if(hvy[x]) build_chain(hvy[x],tp);
    for(int j=fir[x];j;j=nxt[j]) if(son[j]!=pre[x])
     if(son[j]!=hvy[x]) build_chain(son[j],son[j]); 
}
LL Chain_Query(int x,int y){
    LL res=INF;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        res=min(res,Query(root,1,n,pos[top[x]],pos[x]));
        x=pre[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    return min(res,Query(root,1,n,pos[x],pos[y]));
}
int LCA(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        x=pre[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y); return x;
}
LL Chain_Update(int _lca,int x,LL k,LL b){
    while(top[x]!=top[_lca]){
        Updata(root,1,n,pos[top[x]],pos[x],k,b+k*(dep[top[x]]-dep[_lca]));
        x=pre[top[x]];
    }
    Updata(root,1,n,pos[_lca],pos[x],k,b);
}
void Solve(int u,int v,LL _a,LL _b){
    int lca=LCA(u,v);  
    Chain_Update(lca,u,-_a,_b+_a*(dep[u]-dep[lca]));  
    Chain_Update(lca,v,_a,_b+_a*(dep[u]-dep[lca]));  
}
int main(){
    freopen("bzoj4515.in","r",stdin);
    freopen("bzoj4515.out","w",stdout);
    n=getint(); Q=getint();
    for(int i=1;i<=n-1;i++){
        int x=getint(),y=getint(),z=getint();
        add(x,y,z); add(y,x,z); 
    } 
    dfs_info(1); build_chain(1,1);
    root=Build(1,n);
    while(Q--){
        int _type=getint(),x=getint(),y=getint(),_a,_b;  
        if(_type==1) _a=getint(), _b=getint(), Solve(x,y,_a,_b);
                else printf("%lld\n",Chain_Query(x,y));
    }       
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值