BZOJ4515: [Sdoi2016]游戏

41 篇文章 0 订阅
6 篇文章 0 订阅

传送门(这篇写得比我好

qwq
s到t的链可以拆成两条纵链
令dis[x]表示x到根的距离
对于一条纵链上的一个点x,添加的数就可以看成
(dis[x]-dis[t])* k+b
=dis[x]*k-dis[t]*k+b
=k*dis[x]+B
很像直线的表达式y=kx+b
树剖,问题变成线段树维护多条直线在区间内的最小值
对于一条链,因为这个区间内的dis是递增的即x坐标递增,这个操作相当于给线段树一个区间上覆盖一条直线
在区间上打永久化标记为覆盖这个区间的直线,当遇到新的直线时,设加入直线f1,当前覆盖这个区间的直线f2,如果f1(l)< f2(l)&&f1(r)< f2(r) 那么可以用f1代替f2,都大于则没有用,否则递归下去,因为最多只有一个交点,这个交点不在左就在右,所以可以保证复杂度

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline ll _min(const ll a,const ll b){return a<b?a:b;}
inline void down(ll &x,const ll &y){if(x>y)x=y;}
const int maxn = 210000;
const int maxm = 210000;
const ll inf = 123456789123456789ll;

int n,m;
struct edge
{
    int y,c,nex;
    edge(){}
    edge(const int _y,const int _c,const int _nex){y=_y;c=_c;nex=_nex;}
}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y,const int c){a[++len]=edge(y,c,fir[x]);fir[x]=len;}

ll dis[maxn];
int f[maxn],top[maxn],siz[maxn],son[maxn],dep[maxn],w[maxn];
void dfs(const int x)
{
    siz[x]=1;
    for(int k=fir[x];k;k=a[k].nex)
    {
        const int y=a[k].y;
        if(y!=f[x])
        {
            dep[y]=dep[x]+1; dis[y]=dis[x]+(ll)a[k].c;
            f[y]=x;
            dfs(y);
            if(siz[son[x]]<siz[y]) son[x]=y;
            siz[x]+=siz[y];
        }
    }
}
int z;
ll s[maxn];
void build(const int x,const int tp)
{
    s[w[x]=++z]=dis[x]; top[x]=tp;
    if(son[x]) build(son[x],tp);
    for(int k=fir[x];k;k=a[k].nex)
    {
        const int y=a[k].y;
        if(y!=f[x]&&y!=son[x])
            build(y,y);
    }
}

struct segment
{
    bool flag;
    ll a,b,mn;
    segment(){flag=false;mn=inf;}
}seg[maxn<<2];

int lx,rx;
ll fa,fb;
void up(const int x)
{
    int lc=x<<1,rc=lc|1;
    down(seg[x].mn,_min(seg[lc].mn,seg[rc].mn));
}
void upd(const int x,const int l,const int r)
{
    if(rx<l||r<lx) return;
    if(lx<=l&&r<=rx)
    {
        ll cl=s[l]*fa+fb,cr=s[r]*fa+fb;
        if(seg[x].flag) 
        {
            ll cl2=s[l]*seg[x].a+seg[x].b,cr2=s[r]*seg[x].a+seg[x].b;
            if(cl>=cl2&&cr>=cr2) return;
            else if( (cl<cl2&&cr>=cr2)||(cl>=cl2&&cr<cr2) )
            {
                int mid=l+r>>1,lc=x<<1,rc=lc|1;
                upd(lc,l,mid); upd(rc,mid+1,r);
                up(x); return;
            }
        }
        seg[x].flag=true;
        seg[x].a=fa,seg[x].b=fb;
        down(seg[x].mn,_min(cl,cr));
        return;
    }
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    upd(lc,l,mid); upd(rc,mid+1,r);
    up(x);
}
ll ret;
void query(const int x,const int l,const int r)
{
    if(rx<l||r<lx) return;
    if(lx<=l&&r<=rx) { down(ret,seg[x].mn); return ; }
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    if(seg[x].flag)
    {
        ll cl=s[max(lx,l)]*seg[x].a+seg[x].b,cr=s[min(rx,r)]*seg[x].a+seg[x].b;
        down(ret,_min(cl,cr));
    }
    query(lc,l,mid); query(rc,mid+1,r);
}

int LCA(int x,int y)
{
    int f1=top[x],f2=top[y];
    while(f1!=f2)
    {
        if(dep[f1]<dep[f2])  swap(f1,f2),swap(x,y);
        x=f[f1],f1=top[x];
    }
    return dep[x]<dep[y]?x:y;
}
void change(int S,int T,int A,int B)
{
    int tp=LCA(S,T),ft=top[tp];
    int x=S,f1=top[x];
    while(dep[x]>=dep[tp])
    {
        if(f1==ft) f1=tp;
        fa=-A,fb=B+(ll)A*dis[S],lx=w[f1],rx=w[x],upd(1,1,n);
        x=f[f1],f1=top[x];
    }
    int y=T,f2=top[y];
    while(dep[y]>=dep[tp])
    {
        if(f2==ft) f2=tp;
        fa=A,fb=B+(ll)A*(dis[S]-dis[tp]*2ll),lx=w[f2],rx=w[y],upd(1,1,n);
        y=f[f2],f2=top[y];
    }
}
ll solve(int x,int y)
{
    ll re=inf;
    int f1=top[x],f2=top[y];
    while(f1!=f2)
    {
        if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
        lx=w[f1],rx=w[x],ret=inf,query(1,1,n);
        down(re,ret);
        x=f[f1],f1=top[x];
    }
    if(dep[x]>dep[y]) swap(x,y);
    lx=w[x],rx=w[y],ret=inf,query(1,1,n);
    down(re,ret);
    return re;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int x,y,c; scanf("%d%d%d",&x,&y,&c);
        ins(x,y,c); ins(y,x,c);
    }
    dep[1]=1; dfs(1);
    build(1,1);

    for(int i=1;i<=m;i++)
    {
        int type,s,t; scanf("%d%d%d",&type,&s,&t);
        if(type==1)
        {
            int A,B; scanf("%d%d",&A,&B);
            change(s,t,A,B);
        }
        else printf("%lld\n",solve(s,t));
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值