【BZOJ4519】【Sdoi2016】游戏 线段树

第一次接触到这么神奇的线段树。

首先树的形态不改变所以数链剖分+线段树建树。对于线段树上的每一个点,我们保证其最多保存一条直线,如果存在第二条直线,那么两条直线“占据空间”较小的一个一定可以下传到它的某个儿子,而这个节点只存占据空间较大的一个即可。

查询的时候要用到永久化(好像是叫这个奇怪的名字)思想,一边向下查一边比较存放在当前节点的直线能否更新最小值。

 

/************************************************************** 
    Problem: 4515 
    User: RicardoWang 
    Language: C++ 
    Result: Accepted 
    Time:21920 ms 
    Memory:38232 kb 
****************************************************************/
  
#include<cstdlib> 
#include<cstdio> 
#include<iostream> 
#include<cstring> 
#include<cmath> 
#include<algorithm> 
#include<queue> 
#include<vector> 
using namespace std; 
#define oo 123456789123456789ll 
#define maxn 200005 
void _read(int &x){x=0; char ch=getchar(); bool flag=false;while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}if(flag)x=-x; return ;} 
void _readLL(long long &x){x=0; char ch=getchar(); bool flag=false;while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}if(flag)x=-x; return ;} 
  
struct edge{int to,d,next;}e[maxn*2]; int edge_ct,head[maxn]; void add(int x,int y,int z){e[++edge_ct]=(edge){y,z,head[x]};head[x]=edge_ct;e[++edge_ct]=(edge){x,z,head[y]};head[y]=edge_ct;} 
int n,m,son[maxn],sz[maxn],d[maxn],tp[maxn],tr[maxn],f[maxn],_time,_tr[maxn],fd[maxn]; 
long long dis[maxn]; 
void DFS_1(int now,int fa) 
{ 
    sz[now]=1; son[now]=0; d[now]=d[fa]+1; f[now]=fa; dis[now]=dis[fa]+fd[now]; 
    for(int id=head[now];id;id=e[id].next){if(e[id].to==fa)continue;fd[e[id].to]=e[id].d;DFS_1(e[id].to,now);sz[now]+=sz[e[id].to];if(sz[e[id].to]>sz[son[now]])son[now]=e[id].to;} return ; 
} 
void DFS_2(int now,int top) 
{ 
    tr[now]=++_time;tp[now]=top; if(son[now])DFS_2(son[now],top); 
    for(int id=head[now];id;id=e[id].next){if(e[id].to==f[now] || e[id].to==son[now])continue; DFS_2(e[id].to,e[id].to);}return ; 
} 
void Init(){int x,y,z;_read(n);_read(m);for(int i=1;i<n;i++){_read(x);_read(y);_read(z);add(x,y,z);}DFS_1(1,0);DFS_2(1,1);return ;} 
int np,rt,chi[2*maxn][2];bool tag[maxn*2]; 
long long d_first[2*maxn],dsum[2*maxn],minv[maxn]; 
void build(int &now,int L,int R){now=++np; tag[now]=false; minv[now]=oo;if(L==R){d_first[now]=dsum[now]=fd[_tr[L]];return;}int m=(L+R)>>1;build(chi[now][0],L,m);build(chi[now][1],m+1,R);d_first[now]=d_first[chi[now][0]];dsum[now]=dsum[chi[now][0]]+dsum[chi[now][1]];} 
int LCA(int x,int y){   while(tp[x]!=tp[y]){if(d[tp[x]]>=d[tp[y]])x=f[tp[x]];else y=f[tp[y]];} return d[x]>d[y] ? y:x;} 
long long getsum(int now,int L,int R,int x,int y) 
{ 
    if(x<=L && R<=y)return dsum[now]; 
    long long t1=0,t2=0;    int m=(L+R)>>1; 
    if(x<=m)t1=getsum(chi[now][0],L,m,x,y); 
    if(y>m)t2=getsum(chi[now][1],m+1,R,x,y); 
    return t1+t2; 
} 
long long _A[maxn*2],_B[maxn*2]; 
void pushtag(int now,int L,int R,long long A,long long B) 
{ 
    if(!tag[now]){  tag[now]=true; _A[now]=A; _B[now]=B ;} 
    else
    { 
        if(A==_A[now]) 
        { 
            _B[now]=min(_B[now],B); 
        } 
        else
        { 
            if(A>_A[now]){swap(A,_A[now]);swap(B,_B[now]);} 
            long long T=(B-_B[now])/(_A[now]-A); 
            while(T*(_A[now]-A)<=(B-_B[now]))T++; 
            if(T<=d_first[now]) 
            { 
                _A[now]=A; _B[now]=B; 
            } 
            else if(T>dsum[now]) 
            { 
                  
            } 
            else
            { 
                if(T<=dsum[chi[now][0]]) 
                { 
                    pushtag(chi[now][0],L,(L+R)/2,_A[now],_B[now]); 
                    _A[now]=A; _B[now]=B; 
                } 
                else
                { 
                    pushtag(chi[now][1],(L+R)/2+1,R,A,B+A*dsum[chi[now][0]]); 
                } 
            } 
        } 
    } 
    return ; 
} 
long long update(int now,int L,int R,int x,int y,long long A,long long B) 
{    
    if(x<=L && R<=y) 
    { 
        pushtag(now,L,R,A,B); 
        minv[now]=min(minv[now],A>=0 ? A*d_first[now]+B : A*dsum[now]+B); 
        return dsum[now]; 
    } 
    int m=(L+R)>>1; long long tmp=0; 
    if(x<=m)tmp=update(chi[now][0],L,m,x,y,A,B); 
    if(y>m)tmp=tmp+update(chi[now][1],m+1,R,x,y,A,B+tmp*A); 
    minv[now]=min(minv[now],min(minv[chi[now][0]],minv[chi[now][1]])); 
    return tmp; 
} 
long long get(int now,int L,int R,int x) 
{ 
    if(!tag[now])return oo; 
    else return _A[now]*getsum(rt,1,n,L,x)+_B[now]; 
} 
long long query(int now,int L,int R,int x,int y) 
{ 
    long long ans=oo; 
    if(tag[now] && x<=R && y>=L)ans=min(ans,_A[now]>=0? get(now,L,R,max(x,L)):get(now,L,R,min(y,R))); 
    if(x<=L && R<=y) 
    { 
        return min(ans,minv[now]); 
    } 
    int m=(L+R)>>1; 
    if(x<=m)ans=min(ans,query(chi[now][0],L,m,x,y)); 
    if(y>m)ans=min(ans,query(chi[now][1],m+1,R,x,y)); 
    return ans; 
} 
void op1(int x,int y,long long a,long long b) 
{ 
    int z=LCA(x,y); 
    long long dissum=0; 
    while(tp[x]!=tp[z]) 
    { 
        dissum+=getsum(rt,1,n,tr[tp[x]],tr[x]); 
        update(rt,1,n,tr[tp[x]],tr[x],-a,dissum*a+b);        
        x=f[tp[x]]; 
    } 
    dissum+=getsum(rt,1,n,tr[z],tr[x]);  
    update(rt,1,n,tr[z],tr[x],-a,dissum*a+b); 
    dissum-=fd[z]; 
    dissum+=dis[y]-dis[z]; 
    while(tp[y]!=tp[z]) 
    { 
        dissum-=getsum(rt,1,n,tr[tp[y]],tr[y]); 
        update(rt,1,n,tr[tp[y]],tr[y],a,dissum*a+b); 
        y=f[tp[y]]; 
    } 
    if(y!=z){dissum-=getsum(rt,1,n,tr[son[z]],tr[y]);update(rt,1,n,tr[son[z]],tr[y],a,dissum*a+b);} 
    return ; 
} 
char s[35];int cc; 
void out(long long x) 
{ 
    if(!x)putchar('0'); 
    if(x<0){x=-x;putchar('-');} 
    cc=0; while(x){s[++cc]=x%10+'0'; x=x/10;} 
    while(cc){putchar(s[cc]);cc--;} 
    putchar('\n'); 
    return ; 
} 
void op2(int x,int y) 
{ 
    int z=LCA(x,y); 
    long long ans=oo; 
    while(tp[x]!=tp[z]) 
    { 
        ans=min(ans,query(rt,1,n,tr[tp[x]],tr[x])); x=f[tp[x]]; 
    } 
    ans=min(ans,query(rt,1,n,tr[z],tr[x])); 
    while(tp[y]!=tp[z]) 
    { 
        ans=min(ans,query(rt,1,n,tr[tp[y]],tr[y])); y=f[tp[y]]; 
    } 
    ans=min(ans,query(rt,1,n,tr[z],tr[y])); 
    out(ans); 
    return ; 
} 
void work() 
{ 
    for(int i=1;i<=n;i++)_tr[tr[i]]=i; 
    build(rt,1,n); 
    char op;int s,t;long long a,b; 
    int cct=0; 
    for(int i=1;i<=m;i++) 
    { 
        op=getchar(); while(op!='1'&&op!='2')op=getchar(); 
        if(op=='1') 
        { 
            if(i==2458) 
            { 
                int hh; 
                hh++; 
            } 
            _read(s); _read(t); _readLL(a); _readLL(b); op1(s,t,a,b); 
        } 
        else
        { 
            cct++; 
            if(i==2459) 
            { 
                int hh; 
                hh++; 
            } 
            _read(s); _read(t);/* s=110;t=110;printf("%d ",i);*/op2(s,t); 
        } 
    } 
    return ; 
} 
int main() 
{ 
    //freopen("in.txt","r",stdin); 
    //freopen("out.txt","w",stdout); 
    Init(); 
    work(); 
    return 0; 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值