bzoj 4515: [Sdoi2016]游戏

来源:互联网 发布:软件就业班 编辑:程序博客网 时间:2024/06/05 12:47

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

题解

我们可以吧一个修改拆成两个
S–>LCA
LCA—->T
那么就是变成了插入一次函数,然后求最大了
用一个树剖+超哥线段树就可以了
不会超哥线段树的可以做我博客的上一题
CODE:

#include<cstdio>#include<cstdlib>#include<stack>#include<iostream>#include<algorithm>#include<cstring>typedef long long LL;using namespace std;const LL N=100005;typedef long long LL;struct qq{    LL x,y,z,last;}e[N*2];LL num,last[N];LL n,m;void init (LL x,LL y,LL z){    num++;    e[num].x=x;e[num].y=y;e[num].z=z;    e[num].last=last[x];    last[x]=num;}LL dep[N],ys[N],top[N],tot[N],fa[N],son[N];LL dis[N];void dfs (LL x,LL ff){    tot[x]=1;    for (LL u=last[x];u!=-1;u=e[u].last)    {        LL y=e[u].y;        if (y==ff) continue;        dep[y]=dep[x]+1;        dis[y]=dis[x]+e[u].z;        fa[y]=x;        dfs(y,x);        tot[x]+=tot[y];        if (tot[son[x]]<tot[y]) son[x]=y;    }}LL num2;LL g[N];//线段树这一条边对应的是什么 void dfs1 (LL x,LL tp){    ys[x]=++num2;g[num2]=dis[x];    top[x]=tp;    if (son[x]!=0) dfs1(son[x],tp);    for (LL u=last[x];u!=-1;u=e[u].last)    {        LL y=e[u].y;        if (y==son[x]||y==fa[x]) continue;        dfs1(y,y);    }}struct qt{    LL l,r;    LL s1,s2;    LL a,b;//所用线段的a和b    LL c;//最小的答案}tr[N*2];void bt(LL l,LL r){    LL a=++num;    tr[a].l=l;tr[a].r=r;    tr[a].c=123456789123456789LL;    tr[a].a=123456789123456789LL;    tr[a].b=0;    if (l==r) return ;    LL mid=(l+r)>>1;    tr[a].s1=num+1;bt(l,mid);    tr[a].s2=num+1;bt(mid+1,r);}void Bt()//树剖 {    dis[1]=0;dep[1]=0;fa[1]=0;dfs(1,0);/*  for (LL u=1;u<=n;u++)        printf("%lld ",dep[u]);*/    num2=0;dfs1(1,1);    num=0;bt(1,num2);}LL get_LCA (LL x,LL y)//用树剖找出LCA {    LL tx=top[x],ty=top[y];    if (dep[tx]>dep[ty]) {swap(x,y);swap(tx,ty);}    while (tx!=ty)    {        y=fa[ty];        ty=top[y];        if (dep[tx]>dep[ty]) {swap(x,y);swap(tx,ty);}    }    if (dep[x]>dep[y]) swap(x,y);    return x;}LL get (LL a,LL b,LL pos){return a+b*g[pos];}bool Jud (LL a1,LL b1,LL a2,LL b2,LL pos)//是否更小 {    return get(a1,b1,pos)<get(a2,b2,pos);}void change (LL now,LL l,LL r,LL a,LL b,bool tf)//在这一段插入了   是否是成段 {//  printf("HEHE:%lld %lld %lld %lld\n",tr[now].l,tr[now].r,a,b);    if (tr[now].l==l&&tr[now].r==r) tf=true;    LL s1=tr[now].s1,s2=tr[now].s2;    LL mid=(tr[now].l+tr[now].r)>>1;    if (tf==true)//在这个情况下l,r已经没有用了     {        if (tr[now].l==tr[now].r)//到达叶子        {            if (!Jud(tr[now].a,tr[now].b,a,b,tr[now].l))            {                tr[now].c=get(a,b,tr[now].l);                tr[now].a=a,tr[now].b=b;            }        /*printf("Shen:%lld %lld %lld\n",tr[now].l,tr[now].r,tr[now].c);            system("pause");*/            return;        }        if (b<tr[now].b)//如果我现在的斜率更小         {            if (Jud(tr[now].a,tr[now].b,a,b,mid))//如果现在我还不够优,那么只有可能在右边                 change(s2,l,r,a,b,tf);            else//如果我现在已经更优了             {change(s1,l,r,tr[now].a,tr[now].b,tf);tr[now].a=a;tr[now].b=b;}        }        else//我现在的斜率并没有更小         {            if (Jud(tr[now].a,tr[now].b,a,b,mid))//如果我现在不够优                change(s1,l,r,a,b,tf);            else//如果我已经够优了                 {change(s2,l,r,tr[now].a,tr[now].b,tf);tr[now].a=a;tr[now].b=b;}        }        tr[now].c=min(get(tr[now].a,tr[now].b,tr[now].l),get(tr[now].a,tr[now].b,tr[now].r));        tr[now].c=min(tr[now].c,min(tr[s1].c,tr[s2].c));    /*  printf("YES:%lld %lld %lld %lld %lld\n",tr[now].l,tr[now].r,tr[now].c,tr[now].a,tr[now].b);        system("pause");*/        return ;    }    if (r<=mid) change(s1,l,r,a,b,tf);    else if (l>mid) change(s2,l,r,a,b,tf);    else change(s1,l,mid,a,b,tf),change(s2,mid+1,r,a,b,tf);        tr[now].c=min(get(tr[now].a,tr[now].b,tr[now].l),get(tr[now].a,tr[now].b,tr[now].r));        tr[now].c=min(tr[now].c,min(tr[s1].c,tr[s2].c));    /*printf("NO:%lld %lld %lld\n",tr[now].l,tr[now].r,tr[now].c);    system("pause");*/}void Add (LL x,LL y,LL a,LL b)//这两个点插入了一条a,b的线段 {    /*printf("lalal:%lld %lld %lld %lld\n",x,y,a,b);    system("pause");*/    LL tx=top[x],ty=top[y];    if (dep[tx]>dep[ty]) {swap(tx,ty);swap(x,y);}    while (tx!=ty)    {        change(1,ys[ty],ys[y],a,b,false);        y=fa[ty];ty=top[y];        if (dep[tx]>dep[ty]) {swap(x,y);swap(tx,ty);}//让y来跳    }    if (dep[x]>dep[y]) swap(x,y);    change(1,ys[x],ys[y],a,b,false);}LL ans;void get_min (LL now,LL l,LL r){    //printf("get_min:%lld %lld %lld %lld %lld\n",l,r,tr[now].l,tr[now].r,tr[now].c);/*  printf("get_min:l:%lld r:%lld L:%lld R:%lld a:%lld b:%lld c:%lld\n",l,r,tr[now].l,tr[now].r,tr[now].a,tr[now].b,tr[now].c);    system("pause");*/    if (tr[now].l==l&&tr[now].r==r)    {        ans=min(ans,tr[now].c);        return;    }    ans=min(ans,min(get(tr[now].a,tr[now].b,l),get(tr[now].a,tr[now].b,r)));    LL s1=tr[now].s1,s2=tr[now].s2;    LL mid=(tr[now].l+tr[now].r)>>1;    if (r<=mid) get_min(s1,l,r);    else if (l>mid) get_min(s2,l,r);    else get_min(s1,l,mid),get_min(s2,mid+1,r);}void solve (LL x,LL y){    LL tx=top[x],ty=top[y];    if (dep[tx]>dep[ty]) {swap(tx,ty);swap(x,y);}    while (tx!=ty)    {        get_min(1,ys[ty],ys[y]);    //  printf("ans:%lld\n",ans);        y=fa[ty];ty=top[y];        if (dep[tx]>dep[ty]) {swap(x,y);swap(tx,ty);}//让y来跳    }    if (dep[x]>dep[y]) swap(x,y);    get_min(1,ys[x],ys[y]);//  printf("ans:%lld\n",ans);}void solve (){/*  printf("\n");    for (LL u=1;u<=n;u++)        printf("%lld ",g[u]);    printf("\n");*/    while (m--)    {        LL op;        scanf("%lld",&op);        if (op==1)//插入操作         {            LL s,t,a,b;            scanf("%lld%lld%lld%lld",&s,&t,&a,&b);            LL LCA=get_LCA(s,t);            Add(s,LCA,a*dis[s]+b,-a);            Add(LCA,t,a*dis[s]-2*a*dis[LCA]+b,a);        }        else//询问操作         {            LL s,t;            scanf("%lld%lld",&s,&t);            ans=123456789123456789LL;            solve(s,t);            printf("%lld\n",ans);        }    }}int main(){    num=0;memset(last,-1,sizeof(last));    scanf("%lld%lld",&n,&m);    for (LL u=1;u<n;u++)    {        LL x,y,z;        scanf("%lld%lld%lld",&x,&y,&z);        init(x,y,z);init(y,x,z);    }    Bt();//建树    solve();    return 0;}
原创粉丝点击