[BZOJ4515][SDOI2016] 游戏 - 树链剖分 - 半平面交 - 标记永久化

4515: [Sdoi2016]游戏

Time Limit: 40 Sec   Memory Limit: 256 MB
Submit: 390   Solved: 177
[ Submit][ Status][ Discuss]

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

Source

[ Submit][ Status][ Discuss]

HOME   Back

    显然我们首先应该树链剖分一下,这样就能将每个操作1转化成在许多个区间上的操作。那么这些操作是什么呢?显然,我们只要以任意点为根,求出每个点到根的距离deep[],就可以将每个操作1在其路径上的一点q的操作转化成deep[q]*x+y。我们考虑求出每个操作1的起点u和终点v的lca--p,则u-->p上有x=-a,y=a*deep[fr]+b,p-->v上有x=a,y=a*(deep[fr]-2*deep[p])+b。这样我们就相当于在线段树上维护许多条线段在对应点的最小值。

    处理这个问题,只要标记永久化一下就可以了,对于线段树上每个点记录其左/右端点最小时所在的直线,这样更新不下放标记的时间复杂度是O(log n)的,而查询只需要查询整棵线段树所走路径范围内的最小值即可(同一条链上的深度deep显然递增),总时间复杂度O(n log^3 n),但由于常数极小就可以A了。

    zzt线段树写了一个if(l==r&&s==t)导致很惨很惨 =-= 果然还是太弱辣

/**************************************************************
    Problem: 4515
    User: whzzt
    Language: C++
    Result: Accepted
    Time:13172 ms
    Memory:29268 kb
****************************************************************/
 
#include"bits/stdc++.h"
typedef long long ll;
using namespace std;
const int N=100005,LG=20;
const ll inf=123456789123456789ll;
#define fge getchar()
template <class MyInt>
inline void read(MyInt&x){
    x=0;int f=1;char ch=fge;
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=fge;}
    while(ch<='9'&&ch>='0')x=x*10+ch-'0',ch=fge;
    x=x*f;
}
 
struct Line {
    ll a,b;
    Line (ll _=0,ll __=0)
    { a=_; b=__;}
    inline ll F(ll x){return a*x+b;}
} li[N*2];int cnt;
 
ll mn[4*N];int flag[4*N];
int n,m,h[N],to[2*N],nxt[2*N],d[2*N],tmp;
int size[N],fa[N][LG],bel[N],dfn[N],tot;
ll deep[N],va[N],DEEP[N];
 
int lca(int x,int y){
    if(DEEP[x]<DEEP[y])swap(x,y);
    int dx=DEEP[x],dy=DEEP[y],l=dx-dy;
    for(int i=0;l;i++)if(l&(1<<i))x=fa[x][i],l^=(1<<i);
    for(int i=17;x!=y&&i>=0;i--)
        if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return x==y?x:fa[x][0];
}//O(log n)
 
void dfs1(int x){
    for(int i=1;fa[x][i-1];fa[x][i]=fa[fa[x][i-1]][i-1],i++);
    for(int i=h[x];i;i=nxt[i]){
        if(to[i]!=fa[x][0]){
            DEEP[to[i]]=DEEP[x]+1;
            deep[to[i]]=deep[x]+d[i];
            fa[to[i]][0]=x;dfs1(to[i]);
            size[x]+=size[to[i]];
        }
    }
    size[x]++;
}//O(n)
 
void dfs2(int x,int b){
    int go=-1,siz=0,i;
    bel[x]=b;dfn[x]=++tot;va[dfn[x]]=deep[x];
    for(i=h[x];i;i=nxt[i])
        if(to[i]!=fa[x][0]&&size[to[i]]>siz)
            go=to[i],siz=size[to[i]];
    if(go==-1)return;dfs2(go,b);
    for(i=h[x];i;i=nxt[i])
        if(to[i]!=go&&to[i]!=fa[x][0])
            dfs2(to[i],to[i]);
}//O(n)
 
void build(int x,int l,int r){
    int mid=(l+r)>>1,ls=x<<1,rs=ls|1;mn[x]=inf;
    if(l!=r)build(ls,l,mid),build(rs,mid+1,r);
}//O(n)
 
void init(){
    int i,u,v,dis;
    read(n),read(m);
    li[0]=Line(0,inf);
    for(i=1;i<n;i++){
        read(u),read(v),read(dis);
        to[++tmp]=v;nxt[tmp]=h[u];h[u]=tmp;d[tmp]=dis;
        to[++tmp]=u;nxt[tmp]=h[v];h[v]=tmp;d[tmp]=dis;
    }
    dfs1(1);dfs2(1,1);build(1,1,n);
}//O(n)
 
inline void maintain(int x,int l,int r){
    if(l<r)mn[x]=min(mn[x<<1],mn[x<<1|1]);else mn[x]=inf;
    mn[x]=min(mn[x],min(li[flag[x]].F(va[l]),li[flag[x]].F(va[r])));
}//O(1)
 
void up(int t,int y,int l,int r){
    int mid=(l+r)>>1,x=flag[t];
    ll lk=li[x].a,lb=li[x].b,u=li[y].a,v=li[y].b;
    ll x1=va[l]*u+v,y1=va[r]*u+v,x2=va[l]*lk+lb,y2=va[r]*lk+lb;
    if(x1<=x2&&y1<=y2)flag[t]=y;
    else if(x1>=x2&&y1>=y2)return ;
    else {
        if(u<lk){
            ll tmp=(v-lb)/(lk-u)+1;
            if(tmp<=va[mid])swap(y,flag[t]),up(t<<1,y,l,mid);
            else up(t<<1|1,y,mid+1,r);
        } else{
            ll tmp=(lb-v-1)/(u-lk);
            if (tmp>va[mid])swap(y,flag[t]),up(t<<1|1,y,mid+1,r);
            else up(t<<1,y,l,mid);
        }
    }
    maintain(t,l,r);
}//O(logn)
 
void add(int x,int l,int r,int s,int t,int id){
    if(l==s&&r==t){up(x,id,l,r);return;}
    int mid=(l+r)>>1,ls=x<<1,rs=ls|1;
    if(t<=mid)add(ls,l,mid,s,t,id);else
    if(s>mid)add(rs,mid+1,r,s,t,id);else
    add(ls,l,mid,s,mid,id),add(rs,mid+1,r,mid+1,t,id);
    maintain(x,l,r);
}//O(log^2n)
 
inline void paint(int fr,int to,int a,int b){
    int p=lca(fr,to),u=fr,v=to;
    li[++cnt]=Line(-a,a*(ll)deep[fr]+b);
    while(bel[u]!=bel[p]){
        add(1,1,n,dfn[bel[u]],dfn[u],cnt);
        u=fa[bel[u]][0];
    }
    add(1,1,n,dfn[p],dfn[u],cnt);
    li[++cnt]=Line(a,(ll)a*(deep[fr]-2*deep[p])+b);
    while(bel[v]!=bel[p]){
        add(1,1,n,dfn[bel[v]],dfn[v],cnt);
        v=fa[bel[v]][0];
    }
    add(1,1,n,dfn[p],dfn[v],cnt);
}//O(log^3n)
 
ll qrymn(int x,int l,int r,int s,int t){
    if(l==s&&r==t)return mn[x];
    int mid=(l+r)>>1,ls=x<<1,rs=ls|1;
    ll now=min(li[flag[x]].F(va[s]),li[flag[x]].F(va[t])),last=inf;
    if(t<=mid)last=qrymn(ls,l,mid,s,t);
    else if(s>mid)last=qrymn(rs,mid+1,r,s,t);
    else last=min(qrymn(ls,l,mid,s,mid),qrymn(rs,mid+1,r,mid+1,t));
    return min(last,now);
}//O(log n)
 
inline ll ask(int fr,int to){
    ll res=inf;int p=lca(fr,to),u=fr,v=to;
    while(bel[u]!=bel[p]){
        res=min(res,qrymn(1,1,n,dfn[bel[u]],dfn[u]));
        u=fa[bel[u]][0];
    }
    res=min(res,qrymn(1,1,n,dfn[p],dfn[u]));
    while(bel[v]!=bel[p]){
        res=min(res,qrymn(1,1,n,dfn[bel[v]],dfn[v]));
        v=fa[bel[v]][0];
    }
    res=min(res,qrymn(1,1,n,dfn[p],dfn[v]));
    return res;
}//O(log^2n)
 
void work(){
    int i,opt,u,v,a,b;
    for(i=1;i<=m;i++){
        read(opt);read(u),read(v);
        if(opt==1){
            read(a),read(b);
            paint(u,v,a,b);
        } else {
            printf("%lld\n",ask(u,v));
        }
    }
}//O(nlog^3n)
 
int main(){
    init();work();
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值