Bzoj4515 [Sdoi2016]游戏
2017-04-08 19:48
295 查看
Submit: 594 Solved: 234
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 51 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
1234567891234567896
-106
HINT
n≤100000,m≤100000,∣a∣≤10000,0<=w,|b|<=10^9
Source
树链剖分+李超线段树
似乎是传说中的线段树维护半平面交。
在从s到t的路径上的每一个点上都添加一个值,添加的值满足某个函数关系;
询问从s到t的路径上最小的值。
如果把从s到t的路径从树上拎出来,那就得到了一条链,这条链可以看作是以s为原点的数轴,在数轴上脑补一个平面直角坐标系,我们要做的其实是往坐标系中添加线段,并查询某个横坐标x对应的最小的线段纵坐标f(x)。
拎一条链出来,当然就是树链剖分了。
接下来在线段树上维护线段(好像略喜感)。在线段树的每个结点上,存长度大于该结点对应区间的最优的线段。
当更新区间的时候,如果新线段完全比结点上存的旧线段优或者劣,就可以替换/舍弃。但如果只是一部分比旧线段优,就应该把占优比例更多的那条留在当前结点,把另一条下传到子区间递归更新。
询问的时候,由于每一层都存有线段,在每一层都需要更新。
我的线段树写法是(L<=l && r<=R)时更新,结点最小值取min(F(L),F(R)),在这个问题中,显然如果线段的左右端点在l和L之间,这样更新会记录下实际不属于该结点对应区间的值。
解决办法是最小值取min (F(max(L,l)),F(min(r,R)) )
而我并没有意识到这个显然的问题,加上另一些细节问题,WA记录成功刷了半页
CGOS提供数据真是良心
肝了差不多两晚上才切掉,终于可以安心刷会儿邦邦了
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<cmath> #define LL long long using namespace std; const LL INF =123456789123456789LL; const int mxn=200010; LL read(){ LL x=0,f=1;char ch=getchar(); while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void write(LL x){ if(x>9)write(x/10); putchar(x%10+'0'); return; } // struct edge{ int v,nxt,w; }e[mxn<<1]; int hd[mxn],mct=0; void add_edge(int u,int v,int w){ e[++mct].v=v;e[mct].nxt=hd[u];e[mct].w=w;hd[u]=mct;return; } // struct node{ int fa,son,top; int w,e; }t[mxn]; int sz[mxn],dep[mxn],tct=0; int mp[mxn]; LL dis[mxn]; void DFS1(int u,int fa){ sz[u]=1; dep[u]=dep[fa]+1; for(int i=hd[u],v;i;i=e[i].nxt){ v=e[i].v; if(v==fa)continue; t[v].fa=u; dis[v]=dis[u]+e[i].w; DFS1(v,u); sz[u]+=sz[v]; if(sz[v]>sz[t[u].son])t[u].son=v; } return; } void DFS2(int u,int top){ // printf("dfs:%u\n",u); t[u].w=++tct; t[u].top=top; mp[tct]=u;//反向映射 if(t[u].son){ DFS2(t[u].son,top); for(int i=hd[u],v;i;i=e[i].nxt){ v=e[i].v; if(v==t[u].fa || v==t[u].son)continue; DFS2(v,v); } } t[u].e=tct; return; } int LCA(int x,int y){ while(t[x].top!=t[y].top){ if(dep[t[x].top]<dep[t[y].top])swap(x,y); x=t[t[x].top].fa; } return dep[x]<dep[y]?x:y; } // struct SGT{ int l,r; LL a,b,mini; }st[mxn<<2]; int cnt=0,rot; int n,m; inline LL F(LL a,LL b,int x){ return a*dis[mp[x]]+b; } inline void pushup(int rt){ st[rt].mini=min(st[rt].mini,min(st[st[rt].l].mini,st[st[rt].r].mini)); return; } void Build(int l,int r,int &rt){ if(!rt)rt=++cnt; st[rt].a=0; st[rt].b=st[rt].mini=INF; if(l==r)return; int mid=(l+r)>>1; Build(l,mid,st[rt].l); Build(mid+1,r,st[rt].r); // pushup(rt); return; } void modify(LL a,LL b,int l,int r,int rt){ LL fL=F(st[rt].a,st[rt].b,l); LL fR=F(st[rt].a,st[rt].b,r); LL nL=F(a,b,l); LL nR=F(a,b,r); if(fL>=nL && fR>=nR){//完全更优 st[rt].a=a;st[rt].b=b;return; } if(fL<=nL && fR<=nR)return;//完全更差 int mid=(l+r)>>1; LL fM=F(st[rt].a,st[rt].b,mid); LL nM=F(a,b,mid); if(fM>nM){ swap(st[rt].a,a);swap(st[rt].b,b); swap(fL,nL);swap(fR,nR); } if(fL>=nL)modify(a,b,l,mid,st[rt].l); if(fR>=nR)modify(a,b,mid+1,r,st[rt].r); // pushup(rt); return; } void update(LL a,LL b,int L,int R,int l,int r,int rt){ st[rt].mini=min(st[rt].mini,min(F(a,b,max(L,l)),F(a,b,min(r,R))));//取正确端点 if(L<=l && r<=R){ modify(a,b,l,r,rt); return; } int mid=(l+r)>>1; if(L<=mid)update(a,b,L,R,l,mid,st[rt].l); if(R>mid)update(a,b,L,R,mid+1,r,st[rt].r); // pushup(rt); return; } LL query(int L,int R,int l,int r,int rt){ // printf("%lld %lld %lld L:%d R:%d l:%d r:%d rt:%d\n", // st[rt].a,st[rt].b,st[rt].mini,L,R,l,r,rt); LL res=INF; res=min(F(st[rt].a,st[rt].b,max(L,l)),F(st[rt].a,st[rt].b,min(r,R))); if(L<=l && r<=R){ res=min(res,st[rt].mini); return res; } int mid=(l+r)>>1; if(L<=mid)res=min(res,query(L,R,l,mid,st[rt].l)); if(R>mid)res=min(res,query(L,R,mid+1,r,st[rt].r)); return res; } void T_add(int x,int y,LL a,LL b){ while(t[x].top!=t[y].top){ if(dep[t[x].top]<dep[t[y].top])swap(x,y); update(a,b,t[t[x].top].w,t[x].w,1,n,rot); x=t[t[x].top].fa; } if(dep[x]>dep[y])swap(x,y); update(a,b,t[x].w,t[y].w,1,n,rot); return; } LL Que(int x,int y){ LL res=INF; while(t[x].top!=t[y].top){ if(dep[t[x].top]<dep[t[y].top])swap(x,y); res=min(res,query(t[t[x].top].w,t[x].w,1,n,rot)); x=t[t[x].top].fa; } if(dep[x]>dep[y])swap(x,y); res=min(res,query(t[x].w,t[y].w,1,n,rot)); return res; } int main(){ // freopen("menci_game.in","r",stdin); // freopen("menci_game.out","w",stdout); int i,j,u,v,w; n=read();m=read(); for(i=1;i<n;i++){ u=read();v=read();w=read(); add_edge(u,v,w); add_edge(v,u,w); } DFS1(1,0); DFS2(1,1); Build(1,n,rot); int op,s,t; LL a,b; for(i=1;i<=m;i++){ op=read(); if(op==1){ s=read();t=read();a=read();b=read(); int tmp=LCA(s,t); T_add(s,tmp,-a,a*dis[s]+b); T_add(tmp,t,a,a*(dis[s]-dis[tmp]*2)+b); } else{ s=read();t=read(); LL res=Que(s,t); printf("%lld\n",res); } } return 0; }
相关文章推荐
- bzoj4515: [Sdoi2016]游戏
- [树链剖分+李超线段树] BZOJ4515: [Sdoi2016]游戏
- BZOJ4515: [Sdoi2016]游戏
- BZOJ4515: [Sdoi2016]游戏
- BZOJ 4515|SDOI 2016|游戏|树链剖分
- 【BZOJ4519】【Sdoi2016】游戏 线段树
- 4515: [Sdoi2016]游戏
- 【BZOJ4515】[Sdoi2016]游戏 树链剖分+线段树
- [BZOJ4515][Sdoi2016]游戏(树链剖分)
- 【bzoj4515】【SDOI2016】【游戏】【线段树+树链剖分】
- 4600: [Sdoi2016]硬币游戏
- bzoj 4515 [Sdoi2016]游戏 线段树维护凸包
- BZOJ 4515 [Sdoi2016]游戏
- [SDOI 2016]游戏
- bzoj4600 [Sdoi2016]硬币游戏
- [bzoj4515] [Sdoi2016]游戏
- 【bzoj4600】【SDOI2016】【硬币游戏】【博弈论+dp】
- bzoj 4515: [Sdoi2016]游戏 树链剖分
- [SDOI 2016] 硬币游戏
- bzoj4600 [Sdoi2016]硬币游戏