[bzoj4515] [Sdoi2016]游戏

  听说叫李超线段树?...

  加线段(或直线)的操作在http://wenku.baidu.com/link?url=sWGcvUR1m_SwkQ7XeZtMGacA9H7UOcPgYCLNDSSu8YSSClVP11fGN4RNaMwcP5Ltr4HKj10izVldQnvaZRtQ6hvPmFKGfDt0MR_i-YduhX7 里有提到。

  题目就是在区间内加线段,然后问区间内最小值。

  加线段主要是判断优势区间。。

  比较一下新加进来的线段,和原来区间内标记的线段,哪条的优势区间(是较小值的区间)大,就把它作为区间内标记的线段。。

  然后把另外一条下放到它优势的子区间内...继续比较。

  新加的线段最多被分为logn条子线段,每条子线段最多下放logn次。单次插入时间复杂度O(log²n)

  这题强行扔到树上的话那就再写一波链剖了....(记得某年GDKOI出题人也是这么无聊...

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<algorithm>
  5 #define ll long long
  6 using namespace std;
  7 const int maxn=100233,mxnode=maxn<<1,mxline=maxn*50;
  8 struct zs{int too,pre,dis;}e[maxn<<1];int tot,last[maxn];
  9 int lc[mxnode],rc[mxnode],id[mxnode],tt;
 10 ll mn[mxnode],dis[maxn];
 11 int dfn[maxn],fa[maxn],dep[maxn],bel[maxn],tim,sz[maxn],disfa[maxn];
 12 ll k[mxline],b[mxline],MN;
 13 int i,j,n,m,cnt,L,R,aa,bb;
 14 ll ST,ST1;
 15
 16 int ra,fh;char rx;
 17 inline int read(){
 18     rx=getchar(),ra=0,fh=1;
 19     while((rx<‘0‘||rx>‘9‘)&&rx!=‘-‘)rx=getchar();
 20     if(rx==‘-‘)fh=-1,rx=getchar();
 21     while(rx>=‘0‘&&rx<=‘9‘)ra*=10,ra+=rx-48,rx=getchar();return ra*fh;
 22 }
 23
 24 inline ll get(int id,int x){return k[id]*dis[x]+b[id];}
 25 inline void updmn(int x,int a,int b){
 26     if(id[x])
 27         mn[x]=k[id[x]]<0?get(id[x],b):get(id[x],a);//,printf("%d--%d  nowmn:%lld\n",a,b,mn[x]);
 28     else mn[x]=ST1;
 29     if(a<b)mn[x]=min(mn[x],min(mn[lc[x]],mn[rc[x]]));
 30 }
 31 void upd(int x,int a,int b,int now){
 32     if(L<=a&&R>=b){
 33         if(!id[x]){
 34             id[x]=now,updmn(x,a,b);
 35             return;
 36         }
 37         bool f1=get(id[x],a)<get(now,a),f2=get(id[x],b)<get(now,b),f3;
 38         if(f1==f2||a==b){
 39             if(!f1)id[x]=now,updmn(x,a,b);
 40             return;
 41         }
 42         int mid=a+b>>1;
 43         f3=get(id[x],mid)<get(now,mid);
 44         if(f1==f3)
 45             if(f1)upd(rc[x],mid+1,b,now);
 46             else upd(rc[x],mid+1,b,id[x]),id[x]=now;
 47         else
 48             if(f1)upd(lc[x],a,mid,id[x]),id[x]=now;
 49             else upd(lc[x],a,mid,now);
 50     }else{
 51         int mid=a+b>>1;
 52         if(L<=mid)upd(lc[x],a,mid,now);
 53         if(R>mid)upd(rc[x],mid+1,b,now);
 54     }
 55     updmn(x,a,b);
 56 //  printf("%d--%d  mn:%lld\n",a,b,mn[x]);
 57 }
 58 void query(int x,int a,int b){
 59     if(id[x])
 60         if(k[id[x]]<0)MN=min(MN,get(id[x],min(b,R)));else MN=min(MN,get(id[x],max(a,L)));
 61     if(L<=a&&R>=b){
 62         MN=min(MN,mn[x]);return;
 63     }
 64     if(a==b)return;
 65     int mid=a+b>>1;
 66     if(L<=mid)query(lc[x],a,mid);
 67     if(R>mid)query(rc[x],mid+1,b);
 68 }
 69
 70
 71 void dfs(int x){
 72     sz[x]=1,dep[x]=dep[fa[x]]+1;
 73     for(int i=last[x];i;i=e[i].pre)if(e[i].too!=fa[x])
 74         fa[e[i].too]=x,disfa[e[i].too]=e[i].dis,dfs(e[i].too),sz[x]+=sz[e[i].too];
 75 }
 76 void dfs2(int x,int chain,ll now){
 77     bel[x]=chain,dfn[x]=++tim,dis[tim]=now;
 78     int mx=0,i;
 79     for(i=last[x];i;i=e[i].pre)if(e[i].too!=fa[x]&&sz[e[i].too]>sz[mx])mx=e[i].too;
 80     if(!mx)return;
 81     dfs2(mx,chain,now+disfa[mx]);
 82     for(i=last[x];i;i=e[i].pre)if(e[i].too!=fa[x]&&e[i].too!=mx)dfs2(e[i].too,e[i].too,now+disfa[e[i].too]);
 83 }
 84 inline int getlca(int a,int b){
 85     while(bel[a]!=bel[b]){
 86         if(dep[bel[a]]<dep[bel[b]])swap(a,b);
 87         a=fa[bel[a]];
 88     }
 89     return dep[a]<dep[b]?a:b;
 90 }
 91 inline void insert(int a,int b,int c){
 92     e[++tot].too=b,e[tot].dis=c,e[tot].pre=last[a],last[a]=tot,
 93     e[++tot].too=a,e[tot].dis=c,e[tot].pre=last[b],last[b]=tot;
 94 }
 95
 96 void build(int a,int b){
 97     int x=++tt;mn[x]=ST;
 98     if(a==b)return;
 99     int mid=a+b>>1;
100     lc[x]=tt+1,build(a,mid),rc[x]=tt+1,build(mid+1,b);
101 }
102
103 inline void run_1(int x,int lca){
104     ST=bb;
105     while(bel[x]!=bel[lca]){
106         k[++cnt]=-aa,b[cnt]=ST+aa*dis[dfn[x]],//printf("ST:%lld\n",ST),
107         L=dfn[bel[x]],R=dfn[x],upd(1,1,n,cnt),
108         ST+=1ll*(dis[R]-dis[L]+disfa[bel[x]])*aa,x=fa[bel[x]];
109     }//printf("tmpst:%lld\n",ST);
110     k[++cnt]=-aa,b[cnt]=ST+aa*dis[dfn[x]],//printf("ST:  %lld  k:%lld  b:%lld\n",ST,k[cnt],b[cnt]),
111     L=dfn[lca],R=dfn[x],upd(1,1,n,cnt),
112     ST+=1ll*(dis[R]-dis[L])*aa;
113 }
114 inline void run_2(int x,int lca){
115     ST+=1ll*(dis[dfn[x]]-dis[dfn[lca]])*aa;
116     while(bel[x]!=bel[lca]){
117         k[++cnt]=aa,b[cnt]=ST-aa*dis[dfn[x]],//printf("ST:  %lld\n",ST),
118         L=dfn[bel[x]],R=dfn[x],upd(1,1,n,cnt),
119         ST-=1ll*(dis[R]-dis[L]+disfa[bel[x]])*aa,x=fa[bel[x]];
120     }
121     if(x!=lca)
122         k[++cnt]=aa,b[cnt]=ST-aa*dis[dfn[x]],//printf("ST:  %lld\n",ST),
123         L=dfn[lca]+1,R=dfn[x],upd(1,1,n,cnt);
124 }
125 inline void run_query(int x,int lca){
126     while(bel[x]!=bel[lca])
127         L=dfn[bel[x]],R=dfn[x],query(1,1,n),
128         x=fa[bel[x]];
129     L=dfn[lca],R=dfn[x],query(1,1,n);
130 }
131
132 int main(){int id,x,y,lca;
133     ST=123456789123456789LL;ST1=ST;
134     n=read(),m=read();
135     for(i=1;i<n;i++)x=read(),y=read(),insert(x,y,read());
136     dfs(1),dfs2(1,1,0);
137 //  for(i=1;i<=n;i++)printf("i:%d  bel:%d    dis:%lld  dfn:%d\n",i,bel[i],dis[dfn[i]],dfn[i]);
138     build(1,n);
139     while(m--){
140         id=read(),x=read(),y=read(),lca=getlca(x,y);
141 //      printf("x:%d   y:%d  lca:%d\n",x,y,lca);
142         if(id==1)
143             aa=read(),bb=read(),
144             run_1(x,lca),run_2(y,lca);
145         else{
146             MN=ST1,run_query(x,lca),run_query(y,lca);
147             printf("%lld\n",MN);
148         }
149     }
150 //  for(i=1;i<=cnt;i++)printf("line%d:   k:%lld  b:%lld\n",i,k[i],b[i]);
151 }

时间: 2024-10-07 05:30:13

[bzoj4515] [Sdoi2016]游戏的相关文章

【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 的路径,在这条路径上的每一个点上都添加一个数字.对于路径上

【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树)

Time:2016.05.10 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 李超线段树 一开始听faebdc讲,并没有听的很懂ww 后来找到良心博文啊有木有 折越 首先可以把修改转换一下,因为那个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

[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=

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]+

数据结构(树链剖分,线段树):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

数据结构虐哭空巢老人记

数据结构虐哭空巢老人记 前言 \(\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