bzoj2157旅游
题意:
给定有权树,支持单边权修改,路径边权取相反数,路径边权求和,路径边权求最大最小值。
题解:
用link-cut tree
link-cut tree与树链剖分有些类似,都是用某种数据结构维护树链。但也有很大差异:树链剖分是依据子树节点数确定轻重边,一经确定,不能更改,所以用相对静态的线段树维护,常数也较小。而link-cut tree是用来求解动态树问题的,它的链随时可以改变,因此也只能用动态的splay来维护,常数较大。以下简称lct
注意:本数据结构中splay是以节点在lct中的深度为关键字确定顺序的。lct中节点的fa有两个意思,当它不为自己所在splay的根结点时,fa表示其在splay中的父节点,当它为splay中的根节点时,fa表示这整条树链在lct中的父亲节点。
lct有三个基础操作:
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #define N 20010
- #define inc(i,j,k) for(int i=j;i<=k;i++)
- #define INF 0x3fffffff
- using namespace std;
- //general
- int n,m;
- void debug(){
- n=n;
- }
- //edge
- struct e{int t,w,n;}; e es[N*]; int ess,g[N];
- void pe(int f,int t,int w){es[++ess]=(e){t,w,g[f]}; g[f]=ess; es[++ess]=(e){f,w,g[t]}; g[t]=ess;}
- //splay
- int fa[N]/*点的父亲,包括在splay中的父亲和所在splay的父亲*/,c[N][]/*splay中的儿子节点*/;
- int sm[N],mx[N],mn[N],v[N],/*区间维护信息*/dt[N],dts/*中间过程用*/; bool tg[N]/*标记*/;
- inline bool is_root(int x){return fa[x]==||(c[fa[x]][]!=x&&c[fa[x]][]!=x);}//判断是否为所在splay的根节点
- void pushdown(int x){//标记下传。注:本程序中的标记表明本节点已更新子孙未更新
- if(x==)return;
- if(tg[x]){
- int l=c[x][],r=c[x][]; tg[x]^=;
- if(l){tg[l]^=; v[l]=-v[l]; sm[l]=-sm[l]; int t=mx[l]; mx[l]=-mn[l]; mn[l]=-t;}
- if(r){tg[r]^=; v[r]=-v[r]; sm[r]=-sm[r]; int t=mx[r]; mx[r]=-mn[r]; mn[r]=-t;}
- }
- }
- void update(int x){
- if(x==)return;
- int l=c[x][],r=c[x][]; sm[x]=v[x]+sm[l]+sm[r]; mx[x]=max(v[x],max(mx[l],mx[r])); mn[x]=min(v[x],min(mn[l],mn[r]));
- }
- void rotate(int x){//旋转
- if(x==||is_root(x))return;
- int a=fa[x],b=fa[fa[x]]; bool d=c[a][]==x,e=c[b][]==a;
- if(! is_root(a))c[b][e]=x;//注意在调用is_root前不能动参数的fa[a]和c[fa[a]][0]c[fa[a]][1],不然会导致错误结果
- if(c[x][!d])fa[c[x][!d]]=a;/*注意别漏*/ fa[x]=b; fa[a]=x; c[a][d]=c[x][!d]; c[x][!d]=a;//注意修改的相对顺序
- update(a); update(x); if(! is_root(x))update(b);
- }
- void splay(int x){//伸展
- if(x==)return;
- dts=; int t=x;
- while(! is_root(t))dt[++dts]=t,t=fa[t]; dt[++dts]=t;//将根结点到x的所有标记一次下传
- while(dts)pushdown(dt[dts]),dts--;
- while(! is_root(x)){
- if(!is_root(fa[x])) (c[fa[x]][]==x)^(c[fa[fa[x]]][]==fa[x])?rotate(x):rotate(fa[x]);
- rotate(x);
- }
- }
- //lct
- int access(int x){//lct基本操作,使节点到根节点连成一条链,并把链上的分支断开
- if(x==)return ;
- int t=;
- while(x){
- splay(x); c[x][]=t; if(t)fa[t]=x;
- update(x); t=x; x=fa[x];
- }
- debug();
- return t;
- }
- void link(int x,int y){//lct基本操作,本程序不用
- if(x==||y==)return;
- access(x); splay(x); fa[x]=y;
- }
- void cut(int x){//lct基本操作,本程序不用
- if(x==)return;
- access(x); splay(x); if(c[x][])fa[c[x][]]=; c[x][]=; update(x);
- }
- //command
- //注意修改时要根据题目要求将修改查询边权变为程序中的修改查询点权
- void change(int x,int val){//更新权值
- splay(x); v[x]=val; update(x);
- }
- void rever(int x,int y){//反转
- access(x); int a=access(y); /*别*/splay(x);/*漏!下同*/
- if(x==a){
- int r=c[x][]; tg[r]^=; v[r]=-v[r]; sm[r]=-sm[r]; int t=mx[r]; mx[r]=-mn[r]; mn[r]=-t; update(x);
- }else{
- tg[x]^=; v[x]=-v[x]; sm[x]=-sm[x]; int t=mx[x]; mx[x]=-mn[x]; mn[x]=-t;
- int r=c[a][]; tg[r]^=; v[r]=-v[r]; sm[r]=-sm[r]; t=mx[r]; mx[r]=-mn[r]; mn[r]=-t;
- update(a);
- }
- }
- int querysum(int x,int y){//求和
- access(x); int a=access(y); splay(x);
- if(x==a)return sm[c[x][]];else return sm[x]+sm[c[a][]];
- }
- int querymax(int x,int y){//求最大值
- access(x); int a=access(y); splay(x);
- if(x==a)return mx[c[x][]];else return max(mx[x],mx[c[a][]]);
- }
- int querymin(int x,int y){//求最小值
- access(x); int a=access(y); splay(x);
- if(x==a)return mn[c[x][]];else return min(mn[x],mn[c[a][]]);
- }
- int num[N];
- void dfs(int x){//建树,并将边权转为点权 ,本程序用边的终点点权表示这条边的边权
- for(int i=g[x];i!=-;i=es[i].n)if(es[i].t!=fa[x]){
- fa[es[i].t]=x; v[es[i].t]=sm[es[i].t]=mx[es[i].t]=mn[es[i].t]=es[i].w; dfs(es[i].t);
- }
- }
- int main(){
- //freopen("big.txt","r",stdin); freopen("big.out","w",stdout);
- memset(num,,sizeof(num)); ess=; memset(g,-,sizeof(g)); memset(fa,,sizeof(fa));
- scanf("%d",&n); inc(i,,n-){int a,b,c; scanf("%d%d%d",&a,&b,&c); pe(a+,b+,c); num[i]=b+;}
- dfs();
- memset(c,,sizeof(c)); memset(tg,,sizeof(tg)); mn[]=INF; mx[]=-INF; scanf("%d",&m); char s[];
- inc(i,,m){
- int a,b; scanf("%s%d%d",s,&a,&b);
- if(s[]=='C')change(num[a],b);
- if(s[]=='N')a++,b++,rever(a,b);
- if(s[]=='S')a++,b++,printf("%d\n",querysum(a,b));
- if(s[]=='A')a++,b++,printf("%d\n",querymax(a,b));
- if(s[]=='I')a++,b++,printf("%d\n",querymin(a,b));
- }
- return ;
- }
20151215
-----------------------------------------------------------------------------------------------------
题解:
用树链剖分重写一下本题。树链剖分主要分3个步骤:
1、第一次dfs,求出各节点的子树大小、权值、深度等信息。
2、第二次dfs,求出各节点所在树链的头,以及各节点的中儿子、在所有树链依次相连组成的大链中的位置
3、将所有树链依次相连组成的大链建成一棵全局线段树
如何求lca(u,v)?
不停循环这样一个过程:u和v哪个深度大,就让它向上爬一条重链再爬一条轻边,直到两个所在链头节点相等(即在同一条树链)。此时,深度小的那个就是lca。
如何维护(查询)(u,v)之间信息?
求lca(u,v),然后对u不停循环这个过程:维护u到所在树链头的信息,接着u爬一条重链再一条轻边,直到树链头深度小于lca(此时u与lca在同一条树链),然后维护u到lca的信息。对v重复相同操作。
程序中我之所以存了每个节点的重儿子,是因为题意求边权,根据我的程序中边权与点权的转换方式,lca的权值不能被维护(查询),所以“维护u到lca的信息”就变成“维护u到lca重儿子的信息(如果此时u=lca,不能执行此操作)”
吐槽:线段树真耗空间,开的数组大小必须是点数的4倍!因为这个我re了5发。同时线段树的pushdown是边查询(修改)边执行的,需要注意。
对比链剖和link-cut tree,发现lct完胜!?不管在代码长度还是时间空间复杂度上都是lct更优。不是说线段树的常数比splay小得多吗?个人认为链剖输在一下几个方面:
1、链剖编程复杂度高,线段树要写一堆操作,链剖本身还要写一堆操作,同时链剖的操作还要写两个循环,大大增大了代码长度。
2、链剖空间复杂度高,4倍点数。这也是引起速度慢的一个重要原因,开大数组在一定程度上会增大程序的耗时。
总结:本题数据不是很大,链剖的适用范围应该是数据比较大的题,因为此时对程序本身常数的要求要高过空间引起的常数。
根本原因:我太弱了,肯定是我链剖写残才会这么慢!
代码:
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #define N 20010
- #define inc(i,j,k) for(int i=j;i<=k;i++)
- #define INF 0x3fffffff
- using namespace std;
- //gerneral
- int n,m;
- //edge
- struct e{int t,w,n;}; e es[N*]; int ess,g[N];
- inline void pe(int f,int t,int w){es[++ess]=(e){t,w,g[f]}; g[f]=ess; es[++ess]=(e){f,w,g[t]}; g[t]=ess;}
- //segment tree
- int sm[N*],mx[N*],mn[N*]/*节点信息*/,v[N][]/*中间数组*/,l[N*],r[N*]/*区间*/;
- int lc[N*],rc[N*]/*左右儿子*/; bool tg[N*]/*标记*/;
- void update(int x){
- if(x==)return;
- sm[x]=sm[lc[x]]+sm[rc[x]]; mx[x]=max(mx[lc[x]],mx[rc[x]]); mn[x]=min(mn[lc[x]],mn[rc[x]]);
- }
- void pushdown(int x){//标记意义同lct
- if(x==||!tg[x])return; int L=lc[x],R=rc[x]; tg[x]^=;
- if(L){tg[L]^=; sm[L]=-sm[L]; int t=mx[L]; mx[L]=-mn[L]; mn[L]=-t;}
- if(R){tg[R]^=; sm[R]=-sm[R]; int t=mx[R]; mx[R]=-mn[R]; mn[R]=-t;}
- }
- void build(int x,int L,int R){//建线段树
- l[x]=L; r[x]=R;
- if(L==R)sm[x]=mx[x]=mn[x]=v[L][],lc[x]=rc[x]=tg[x]=;else{
- int M=(L+R)>>; lc[x]=x<<; rc[x]=x<<|; build(lc[x],L,M); build(rc[x],M+,R); tg[x]=; update(x);
- }
- }
- void change(int x,int nod,int val){//线段树点修改
- pushdown(x);
- if(l[x]==r[x])sm[x]=mx[x]=mn[x]=val;else{
- int M=(l[x]+r[x])>>; nod<=M?change(lc[x],nod,val):change(rc[x],nod,val); update(x);
- }
- }
- void rever(int x,int ql,int qr){//线段树区间修改
- pushdown(x);
- int M=(l[x]+r[x])>>; if(ql<=l[x]&&r[x]<=qr){tg[x]^=; sm[x]=-sm[x]; int t=mx[x]; mx[x]=-mn[x]; mn[x]=-t; return;}
- if(ql<=M)rever(lc[x],ql,qr); if(qr>M)rever(rc[x],ql,qr); update(x);
- }
- int querysum(int x,int ql,int qr){//线段树求和
- pushdown(x);
- int ret=,M=(l[x]+r[x])>>; if(ql<=l[x]&&r[x]<=qr)return sm[x];
- if(ql<=M)ret+=querysum(lc[x],ql,qr); if(qr>M)ret+=querysum(rc[x],ql,qr);
- return ret;
- }
- int querymax(int x,int ql,int qr){//线段树求最大值
- pushdown(x);
- int ret=-INF,M=(l[x]+r[x])>>; if(ql<=l[x]&&r[x]<=qr)return mx[x];
- if(ql<=M)ret=max(ret,querymax(lc[x],ql,qr)); if(qr>M)ret=max(ret,querymax(rc[x],ql,qr));
- return ret;
- }
- int querymin(int x,int ql,int qr){//线段树求最小值
- pushdown(x);
- int ret=INF,M=(l[x]+r[x])>>; if(ql<=l[x]&&r[x]<=qr)return mn[x];
- if(ql<=M)ret=min(ret,querymin(lc[x],ql,qr)); if(qr>M)ret=min(ret,querymin(rc[x],ql,qr));
- return ret;
- }
- //tree chain apart
- int pos[N]/*点在线段树中位置*/,top[N]/*树链头节点*/,fa[N],sz[N]/*子树大小*/,sgs,dep[N]/*深度*/,ps[N]/*重儿子*/;
- void dfs(int x){//得到节点的父亲、子树大小、权值、深度
- sz[x]=;
- for(int i=g[x];i!=;i=es[i].n)if(es[i].t!=fa[x]){
- fa[es[i].t]=x; dep[es[i].t]=dep[x]+; v[es[i].t][]=es[i].w; dfs(es[i].t); sz[x]+=sz[es[i].t];
- }
- }
- void buildchain(int x,int tp/*当前节点所在树链头*/){//通过子树大小构造树链
- pos[x]=++sgs; v[sgs][]=v[x][]; top[x]=tp; ps[x]=;
- for(int i=g[x];i!=;i=es[i].n)if(es[i].t!=fa[x]){
- if(sz[es[i].t]>sz[ps[x]])ps[x]=es[i].t;
- }
- if(ps[x]==)return;
- buildchain(ps[x],tp);
- for(int i=g[x];i!=;i=es[i].n)if(es[i].t!=fa[x]){
- if(es[i].t!=ps[x])buildchain(es[i].t,es[i].t);
- }
- }
- void init(){
- memset(fa,,sizeof(fa)); dep[]=; dfs();
- sgs=; mx[]=-INF; mn[]=INF; sz[]=; buildchain(,); build(,,sgs);
- }
- int lca(int x,int y){
- for(;top[x]!=top[y];x=fa[top[x]]){if(dep[top[x]]<dep[top[y]])swap(x,y);}
- return dep[x]<dep[y]?x:y;
- }
- void solvechange(int x,int val){change(,pos[x],val);}
- void solverever(int x,int y){
- if(x==y)return; int a=lca(x,y);
- while(dep[top[x]]>dep[a])rever(,pos[top[x]],pos[x])/*注意参数顺序,下同*/,x=fa[top[x]];
- if(a!=x)rever(,pos[ps[a]],pos[x]);
- while(dep[top[y]]>dep[a])rever(,pos[top[y]],pos[y]),y=fa[top[y]];
- if(a!=y)rever(,pos[ps[a]],pos[y]);
- }
- int solvesum(int x,int y){
- if(x==y)return ; int a=lca(x,y),ans=;
- while(dep[top[x]]>dep[a])ans+=querysum(,pos[top[x]],pos[x]),x=fa[top[x]];
- if(a!=x)ans+=querysum(,pos[ps[a]],pos[x]);
- while(dep[top[y]]>dep[a])ans+=querysum(,pos[top[y]],pos[y]),y=fa[top[y]];
- if(a!=y)ans+=querysum(,pos[ps[a]],pos[y]);
- return ans;
- }
- int solvemax(int x,int y){
- if(x==y)return ; int a=lca(x,y),ans=-INF;
- while(dep[top[x]]>dep[a])ans=max(ans,querymax(,pos[top[x]],pos[x])),x=fa[top[x]];
- if(a!=x)ans=max(ans,querymax(,pos[ps[a]],pos[x]));
- while(dep[top[y]]>dep[a])ans=max(ans,querymax(,pos[top[y]],pos[y])),y=fa[top[y]];
- if(a!=y)ans=max(ans,querymax(,pos[ps[a]],pos[y]));
- return ans;
- }
- int solvemin(int x,int y){
- if(x==y)return ; int a=lca(x,y),ans=INF;
- while(dep[top[x]]>dep[a])ans=min(ans,querymin(,pos[top[x]],pos[x])),x=fa[top[x]];
- if(a!=x)ans=min(ans,querymin(,pos[ps[a]],pos[x]));
- while(dep[top[y]]>dep[a])ans=min(ans,querymin(,pos[top[y]],pos[y])),y=fa[top[y]];
- if(a!=y)ans=min(ans,querymin(,pos[ps[a]],pos[y]));
- return ans;
- }
- //main
- int num[N];
- int main(){
- //freopen("zs.txt","r",stdin); freopen("zs.out","w",stdout);
- scanf("%d",&n); memset(g,,sizeof(g)); ess=;
- inc(i,,n-){int a,b,c; scanf("%d%d%d",&a,&b,&c); a++; b++; pe(a,b,c); num[i]=b;}
- init(); scanf("%d",&m); char s[];
- inc(i,,m){
- int a,b; scanf("%s%d%d",s,&a,&b);
- if(s[]=='C')solvechange(num[a],b);
- if(s[]=='N')a++,b++,solverever(a,b);
- if(s[]=='S')a++,b++,printf("%d\n",solvesum(a,b));
- if(s[]=='A')a++,b++,printf("%d\n",solvemax(a,b));
- if(s[]=='I')a++,b++,printf("%d\n",solvemin(a,b));
- }
- return ;
- }
20151218
bzoj2157旅游的更多相关文章
- [bzoj2157]旅游 (lct)
这个应该也算裸的模板题吧..主要是边权的问题,对于每条边u->v,我们可以新建一个节点代替他,把边的信息弄到新的点上,就变成u->x->v了... 当然了这样的话要防止u和v这些没用 ...
- BZOJ2157旅游——树链剖分+线段树
题目描述 Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但又为了节约成本,T 城的任意两个景点之间有且只有一条路 ...
- BZOJ2157 旅游 【树剖 或 LCT】
题目 Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但又为了节约成本,T 城的任意两个景点之间有且只有一条路径. ...
- BZOJ2157: 旅游(LCT)
Description Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但又为了节约成本,T 城的任意两个景点之间 ...
- [BZOJ2157]旅游(树链剖分/LCT)
树剖裸题,当然LCT也可以. 树剖: #include<cstdio> #include<algorithm> #define ls (x<<1) #define ...
- BZOJ2157: 旅游
传送门 先讲一个悲伤地故事 RunID User Problem Result Memory Time Language Code_Length Submit_Time 1635823 Cydiate ...
- BZOJ2157: 旅游 树链剖分 线段树
http://www.lydsy.com/JudgeOnline/problem.php?id=2157 在对树中数据进行改动的时候需要很多pushdown(具体操作见代码),不然会wa,大概原因 ...
- 【树链剖分】【线段树】bzoj2157 旅游
#include<cstdio> #include<algorithm> using namespace std; #define INF 2147483647 #define ...
- bzoj2157 旅游——LCT
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2157 仍然是LCT模板题~ 不过有一些需要注意的地方,点和边的区分,0号点的 mx 和 mn ...
随机推荐
- TensorFlow从0到1之TensorFlow实现多元线性回归(16)
在 TensorFlow 实现简单线性回归的基础上,可通过在权重和占位符的声明中稍作修改来对相同的数据进行多元线性回归. 在多元线性回归的情况下,由于每个特征具有不同的值范围,归一化变得至关重要.这里 ...
- 操作-写入excel
xlwt模块 封装 #!/usr/bin/env python # -*- coding: utf-8 -*- import xlwt import xlrd from xlutils.copy im ...
- 测试必备工具之最强抓包神器 Charles,你会了么?
前言 作为软件测试工程师,大家在工作中肯定经常会用到各种抓包工具来辅助测试,比如浏览器自带的抓包工具-F12,方便又快捷:比如时下特别流行的Fiddler工具,使用各种web和APP测试的各种场景 ...
- Linux系统使用Nmon监控及分析系统性能
一.下载nmon(1)查看sever的内核版本: 命令:lsb_release -a执行结果:LSB Version: :base-4.0-amd64:base-4.0-noarch:c ...
- rest_framework django 简单使用(数据库创建数据, 覆盖数据, 其他的大同小异)
事先说几个坑:数据库定义字段时候,不要定义name 要定义 username 首先, 定义model(简单定义) from django.db import models from django.co ...
- pyhton 月份和天数的计算
http://stackoverflow.com/questions/546321/how-do-i-calculate-the-date-six-months-from-the-current-da ...
- TCP实战一(三握四挥、流量控制)
上一篇博文已经介绍了tcpdump的一些基本操作与命令,今天这篇博文将带你解密如何利用wireshark对tcpdump抓到的数据包进行可视化分析! 参考文献:https://zhuanlan.zhi ...
- Tensorflow实现神经网络的前向传播
我们构想有一个神经网络,输入为两个input,中间有一个hidden layer,这个hiddenlayer当中有三个神经元,最后有一个output. 图例如下: 在实现这个神经网络的前向传播之前,我 ...
- 入门大数据---Elasticsearch搭建与应用
项目版本 构建需要: JDK1.7 Elasticsearch2.2.1 junit4.10 log4j1.2.17 spring-context3.2.0.RELEASE spring-core3. ...
- Asp.net Core AOP实现(采用Autofac)
引用正确的库来实现AOP 新的.NET Core是基于.NET Standard的..所以我们在引用库的时候特别要注意相关的兼容问题. 在传统的ASP.NET中,使用过Autofac来进行AOP操作的 ...