P4069 [SDOI2016]游戏

题意

显然书剖套李超树。

考虑怎么算函数值:

设\((x,y)\)的\(lca\)为\(z\),我们插一条斜率为\(k\),截距为\(b\)的线段。

\((x,z)\)上的点\(u\):
\(f(u)=k*(dis[x]-dis[u])+b=-k*dis[u]+(k*dis[x]+b)\)
所以对这条路径插入斜率为\(-k\),截距为\(k*dis[x]+b\)的线段

\((y,z)\)上的点\(u\):
\(f(u)=k*(dis[y]+dis[u]-2*dis[z])+b=k*dis[u]+k*(dis[y]-2*dis[z])+b\)
同理

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define minn(p)(seg[p].minn)
#define minid(p) (seg[p].minid)
const int maxn=100010;
const int inf=123456789123456789ll;
int n,m,cnt,tim;
int head[maxn],dep[maxn],dfn[maxn],pos[maxn],size[maxn],son[maxn],pre[maxn],top[maxn],dis[maxn];
struct edge{int to,nxt,dis;}e[maxn<<1];
struct Line
{
    int k,b;
    inline int calc(int x){return k*x+b;}
};
struct Seg{int minn;Line minid;}seg[maxn<<2];
inline int read()
{
    char c=getchar();int res=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
    return res*f;
}
inline void add(int u,int v,int w)
{
    e[++cnt].nxt=head[u];
    head[u]=cnt;
    e[cnt].to=v;
    e[cnt].dis=w;
}
void dfs1(int x,int fa)
{
    pre[x]=fa;dep[x]=dep[fa]+1;size[x]=1;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(y==fa)continue;
        dis[y]=dis[x]+e[i].dis;
        dfs1(y,x);size[x]+=size[y];
        if(size[son[x]]<size[y])son[x]=y;
    }
}
void dfs2(int x,int tp)
{
    top[x]=tp;dfn[x]=++tim;pos[tim]=x;
    if(son[x])dfs2(son[x],tp);
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(y==pre[x]||y==son[x])continue;
        dfs2(y,y);
    }
}
inline int lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=pre[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    return x;
}
inline void up(int p){minn(p)=min(minn(p),min(minn(ls(p)),minn(rs(p))));}
void build(int p,int l,int r)
{
    minn(p)=inf;minid(p)=(Line){0,inf};
    if(l==r)return;
    int mid=(l+r)>>1;
    build(ls(p),l,mid);build(rs(p),mid+1,r);
}
inline void move(int p,int l,int r,Line id)
{
    int nowl=minid(p).calc(dis[pos[l]]),nowr=minid(p).calc(dis[pos[r]]);
    int tmpl=id.calc(dis[pos[l]]),tmpr=id.calc(dis[pos[r]]);
    if(nowl<=tmpl&&nowr<=tmpr)return;
    if(nowl>=tmpl&&nowr>=tmpr)
    {
        minid(p)=id;minn(p)=min(minn(p),min(tmpl,tmpr));
        return;
    }
    double point=1.0*(minid(p).b-id.b)/(1.0*(id.k-minid(p).k));
    int mid=(l+r)>>1;
    if(tmpl>nowl)
    {
        if(point<=(double)dis[pos[mid]])move(ls(p),l,mid,minid(p)),minid(p)=id;
        else move(rs(p),mid+1,r,id);
    }
    else
    {
        if(point<=(double)dis[pos[mid]])move(ls(p),l,mid,id);
        else move(rs(p),mid+1,r,minid(p)),minid(p)=id;
    }
    minn(p)=min(minn(p),min(tmpl,tmpr));
    up(p);
}
void change(int p,int l,int r,int ql,int qr,Line id)
{
    if(l>=ql&&r<=qr){move(p,l,r,id);return;}
    int mid=(l+r)>>1;
    if(ql<=mid)change(ls(p),l,mid,ql,qr,id);
    if(qr>mid)change(rs(p),mid+1,r,ql,qr,id);
    up(p);
}
int query(int p,int l,int r,int ql,int qr)
{
    if(l>=ql&&r<=qr)return minn(p);
    int mid=(l+r)>>1,res=min(minid(p).calc(dis[pos[max(l,ql)]]),minid(p).calc(dis[pos[min(r,qr)]]));
    if(ql<=mid)res=min(res,query(ls(p),l,mid,ql,qr));
    if(qr>mid)res=min(res,query(rs(p),mid+1,r,ql,qr));
    return res;
}
inline void trchange(int x,int y,int k,int b)
{
    while(top[x]!=top[y])
    {
        change(1,1,n,dfn[top[x]],dfn[x],(Line){k,b});
        x=pre[top[x]];
    }
    change(1,1,n,dfn[y],dfn[x],(Line){k,b});
}
inline void trsolve(int x,int y,int k,int b)
{
    int z=lca(x,y);
    trchange(x,z,-k,k*dis[x]+b);
    trchange(y,z,k,k*(dis[x]-(dis[z]<<1))+b);
}
inline int trquery(int x,int y)
{
    int res=inf;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        res=min(res,query(1,1,n,dfn[top[x]],dfn[x]));
        x=pre[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    res=min(res,query(1,1,n,dfn[x],dfn[y]));
    return res;
}
signed main()
{
    //freopen("test.in","r",stdin);
    //freopen("test.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read(),w=read();
        add(u,v,w),add(v,u,w);
    }
    dfs1(1,0),dfs2(1,1);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int op=read(),x=read(),y=read(),k,b;
        if(op==1)k=read(),b=read(),trsolve(x,y,k,b);
        else printf("%lld\n",trquery(x,y));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/nofind/p/11991893.html

时间: 2024-11-04 14:53:19

P4069 [SDOI2016]游戏的相关文章

[SDOI2016]游戏 树剖+李超树

链接 https://www.luogu.org/problemnew/show/P4069 思路 树剖+超哥线段树 我已经自毙了,自闭了!!!! 代码 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define ll long long using namespace std; const ll N=400007LL; const ll inf=

[bzoj4515] [Sdoi2016]游戏

听说叫李超线段树?... 加线段(或直线)的操作在http://wenku.baidu.com/link?url=sWGcvUR1m_SwkQ7XeZtMGacA9H7UOcPgYCLNDSSu8YSSClVP11fGN4RNaMwcP5Ltr4HKj10izVldQnvaZRtQ6hvPmFKGfDt0MR_i-YduhX7 里有提到. 题目就是在区间内加线段,然后问区间内最小值. 加线段主要是判断优势区间.. 比较一下新加进来的线段,和原来区间内标记的线段,哪条的优势区间(是较小值的区间)大

数据结构(树链剖分,线段树):SDOI 2016 游戏

4515: [Sdoi2016]游戏 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 351  Solved: 157[Submit][Status][Discuss] Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 123456789123456789. 有时,Alice 会选择一条从 s 到 t 的路径,在这条路径上的每一个点上都添加一个数字.对于路径上

BZOJ4513~4518 SDOI2016 R1 题解

4513: [Sdoi2016]储能表 数位dp,f[i][2][2][2]表示前i位,是否卡n的上界,是否卡m的上界,是否卡k的下界,枚举每一维的下一位直接转移. #include<cstdio> #include<cstring> typedef unsigned u32; typedef long long ll; ll x,y,z; int p,q; u32 f[61][2][2][2][2]; int main(){ scanf("%d",&q

【BZOJ-4515】游戏 李超线段树 + 树链剖分 + 半平面交

4515: [Sdoi2016]游戏 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 304  Solved: 129[Submit][Status][Discuss] Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 123456789123456789. 有时,Alice 会选择一条从 s 到 t 的路径,在这条路径上的每一个点上都添加一个数字.对于路径上

数据结构虐哭空巢老人记

数据结构虐哭空巢老人记 前言 \(\cal STO\ f啦sh\ ORZ\) by 去不了冬令营的徐叔叔 搞过的东西就不再写了(数组队列栈链表.线段树动态树替KD树树状数组Splay替罪羊Treap.线段树合并Trie合并.可持久化Trie可持久化线段树.线段树优化DP优化连边) 要写的是 李超线段树 吉利线段树 线段树分裂 虚树 树链的并 还要进一步学习 线段树分治 二进制分组 珂朵莉树 数据结构题注意点 如果更新某点,为了方便访问到其孩子,那么线段树要开8倍空间.否则4倍空间就好了 李超线段

bzoj4600 [Sdoi2016]硬币游戏

Description Alice和Bob现在在玩的游戏,主角是依次编号为1到n的n枚硬币.每一枚硬币都有两面,我们分别称之为正面和反面.一开始的时候,有些硬币是正面向上的,有些是反面朝上的.Alice和Bob将轮流对这些硬币进行翻转操作,且Alice总是先手.具体来说每次玩家可以选择一枚编号为x,要求这枚硬币此刻是反面朝上的.对于编号x来说,我们总可以将x写成x=c*2^a*3^b,其中a和b是非负整数,c是与2,3都互质的非负整数,然后有两种选择第一种,选择整数p,q满足a>=p*q,p>

[SDOI2016]硬币游戏

这道题不难吧,为什么大佬们没有题解呢,一定是dalao们觉得太简单了吧,弄得我好几天才做出来... 很显然,直接按题意模拟即可,求出sg函数,异或和就好了,不知道sg函数的可以自己百度一下...非常神奇的网站 不知道为什么,大佬们都是每次输入n之后再算的sg函数,并且每次算的时候都用的是2的多少次方乘3的多少次方,明明直接求更简单的,效率也并不低... #include<cstdio> #include<cstring> #include<algorithm> usin

20170913自制猜数字游戏

/* 猜数字:系统随机生成一个四位数,请根据下列判断猜出来 A:数值正确,位置正确 B:数值正确,位置不正确 C:数值不正确 */ #include<stdio.h> #include<time.h> #include<stdlib.h> #pragma warning (disable:4996) #define pUCharHead unsigned char * //以数组形式返回n个无重复的随机数,范围可指定[min,max] pUCharHead GenNoR