[SDOI 2016]游戏

题目描述:

雾.

题目分析:

首先可以把修改转换一下,因为那个dis非常不爽。显然s~t的路径有s~lca和lca~t组成。令d[x]表示x的深度,对于s~lca上面的点,修改的值相当于a*(d[s]-d[x])+b=-a*d[x]+(b-a*d[s])
lca~t上面的点的值相当于a*(d[s]+d[x]-2*d[lca])+b=a*d[x]+(b+a*(d[s]-d[lca]*2))
这样就可以得到新的a’和b’,然后一个点的值就相当于a’*d[x]+b’了,这样就只和a’,b’和d[x]有关了。

注意到在树链剖分对应的线段树区间中,相连的一部分它们在树种也是相连的,这样就满足d[]单调递增
观察a’*d[x]+b’,发现这相当于直线f(x)=a’x+b在d[x](两个x不是一个意思)处的取值!
那么就相当于用线段树维护一个区间中的若干条直线在每个部分的最小值。

不妨考虑标记永久化(这里只是一定程度上的永久化但还是要下传的)。让线段树中的一个节点只对应一条直线,那么如果在这个区间加入一条直线怎么办呢?要分类讨论,设新加入的f1(x)=k1x+b1,原来的f2(x)=k2x+b2,左端点为l,右端点为r,那么有:

1.f1(d[l])

题目链接:

Cogs 2222
Luogu 4069

Ac 代码:

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <ctime>
#define ll long long
const int maxm=1e6+100;
const ll inf=123456789123456789ll;
int head[maxm],net[maxm],to[maxm],cost[maxm],cnt;
int son[maxm],deep[maxm],fa[maxm],top[maxm],rank[maxm],id[maxm],size[maxm],root,siz;
int n,m;
ll dis[maxm],v[maxm],minx[maxm];
struct node{
    ll k,b;
    int s;
    node(ll K=0,ll B=0,int S=0){k=K;b=B;s=S;}
    ll calc(int u)
    {
        ll dist=dis[u]-dis[s];
        return k*dist+b;
    }
}st[maxm*4];
void addedge(int x,int y,int w)
{
    cnt++;
    to[cnt]=y,net[cnt]=head[x],head[x]=cnt,cost[cnt]=w;
}
void dfs1(int now,int f,int dep,int last)
{
    fa[now]=f,deep[now]=dep,size[now]=1;
    dis[now]=dis[f]+last;
    for(int i=head[now];i;i=net[i])
    if(to[i]!=f)
    {
        dfs1(to[i],now,dep+1,cost[i]);
        size[now]+=size[to[i]];
        if(size[to[i]]>size[son[now]]) son[now]=to[i];
    }
}
void dfs2(int now,int t)
{
    rank[id[now]=++siz]=now;
    top[now]=t;
    if(son[now]) dfs2(son[now],t);
    for(int i=head[now];i;i=net[i])
    if(!id[to[i]])
     dfs2(to[i],to[i]);
}
void build(int o,int l,int r)
{
    st[o]=(node){0,inf,0};
    minx[o]=inf;
    if(l>=r) return;
    int mid=(l+r)>>1;
    build((o<<1),l,mid),build((o<<1)|1,mid+1,r);
    //minx[o]=std::min(minx[(o<<1)],minx[(o<<1)|1]);
}
int LCA(int u,int v)
{
    while(top[u]!=top[v])
    {
        if(deep[top[u]]<deep[top[v]]) std::swap(u,v);
        u=fa[top[u]];
    }
    return deep[u]<deep[v]?u:v;
}
void insert(int o,int l,int r,node ins)
{
    ll ans=std::min(ins.calc(rank[l]),ins.calc(rank[r]));
    node tmp=st[o];
    if(l==r)
    {
        if(ans<st[o].calc(rank[l])) st[o]=ins,minx[o]=ans;
        return;
    }
    int mid=(l+r)>>1;
    ans=std::min(ans,minx[o]);
    if(ins.k>tmp.k) std::swap(ins,tmp);
    if (ins.calc(rank[mid])<tmp.calc(rank[mid]))
    {
        st[o]=ins,minx[o]=std::min(minx[o],ans);
        insert((o<<1),l,mid,tmp);
    }
    else
    {
        st[o]=tmp,minx[o]=std::min(minx[o],ans);
        insert((o<<1)|1,mid+1,r,ins);
    }
} 
void change(int o,int l,int r,int ql,int qr,node ins)
{
    if(ql<=l&&r<=qr)
    {
        insert(o,l,r,ins);
        return;
    }
    int mid=(l+r)>>1;
    if(ql<=mid) change((o<<1),l,mid,ql,qr,ins);
    if(qr>mid)  change((o<<1)|1,mid+1,r,ql,qr,ins);
    minx[o]=std::min(minx[o],std::min(minx[(o<<1)],minx[(o<<1)|1]));
}
ll ask(int o,int l,int r,int ql,int qr)
{
    int askl=std::max(ql,l),askr=std::min(r,qr);
    ll ans=std::min(st[o].calc(rank[askl]),st[o].calc(rank[askr]));
    //printf("%d %d %d %d %d\n",o,l,r,ql,qr);
    if(ql<=l&&r<=qr) return std::min(ans,minx[o]);
    int mid=(l+r)>>1;
    if (ql<=mid) ans=std::min(ans,ask((o<<1),l,mid,ql,qr));
    if (qr>mid) ans=std::min(ans,ask((o<<1)|1,mid+1,r,ql,qr));
    return ans;
}
void treechange(int u,int v,ll A,ll B)
{
    node ins;
    int lca=LCA(u,v),s=u;
    bool uod=1;
    ll base;
    while(top[u]!=top[v])
    {
        if(deep[top[u]]<deep[top[v]]) std::swap(u,v),uod^=1;
        base=dis[top[u]]+dis[s]-2*dis[lca];
        if(uod==1) base=dis[s]-dis[top[u]];
        base=A*base+B;
        if(!uod) ins=(node){A,base,top[u]};
        else ins=(node){-A,base,top[u]}; 
        change(1,1,n,id[top[u]],id[u],ins);
        u=fa[top[u]];
    }
    if(deep[u]>deep[v]) std::swap(u,v);
    else uod^=1;
    base=dis[u]+dis[s]-2*dis[lca];
    if (uod) base=dis[s]-dis[u];
    base=A*base+B;
    if(!uod) ins=(node){A,base,u};
    else ins=(node){-A,base,u};
    change(1,1,n,id[u],id[v],ins);
}
ll treeask(int u,int v)
{
    ll ans=inf;
    while(top[u]!=top[v])
    {
        if(deep[top[u]]<deep[top[v]]) std::swap(u,v);
        ans=std::min(ans,ask(1,1,n,id[top[u]],id[u]));
        u=fa[top[u]];
    }
    if(deep[u]<deep[v]) std::swap(u,v);
    ans=std::min(ans,ask(1,1,n,id[v],id[u]));
    return ans;
}
int main()
{
    //freopen("menci_game.in","r",stdin);
    //freopen("menci_game.out","w",stdout);
    //srand(time(0));
    scanf("%d%d",&n,&m);
    root=1;
    for(int i=1;i<n;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w),addedge(v,u,w);
    }
    dfs1(1,1,1,0),dfs2(1,1); 
    build(1,1,n);
    //printf("%lld\n",minx[1]);
    for (int i=1;i<=m;i++)
    {
        int opt,u,v,a,b;
        scanf("%d%d%d",&opt,&u,&v);
        if (opt==1)
        {
            scanf("%d%d",&a,&b);
            treechange(u,v,a,b);
        }
        else printf("%lld\n",treeask(u,v));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值