题解:

因为w大于1,所以,题意就是,有多少(x,z),存在x到z的路径上,有一个x<y<z的y

w没用的其实。

树上路径问题,有什么方法吗?

1.树链剖分。这个主要方便处理修改操作。

2.点分治,对于静态无修改点树上统计,非常好用。

3.一些其他的:

利用lca,dfs序,判断点在路径上,点在子树里一些情况。

倍增,处理fa[N][20],dis[N][20] ,

二分再套一个倍增?

4.还有一些灵活应变的:

例如:拆路径为x到lca,lca到y,可以在x,y记录一些lca的信息,把路径就变成了点。

例题:牛客网NOIP赛前集训营-提高组(第一场)T3

这个题,静态无修树上统计,就点分治了。

还可以再带一个log

那么当前层的重心G,统计过G路径。

树形背包思想,直接统计z能和之前的那些x凑成点对,记录x到G路径上的大于x最小的编号nx(因为是存在,不是任意嘛)

然后记录z到G路径上小于z的最大编号pz

如果pz>x,那么可以

如果nx<y,那么可以

但是pz<nx的情况被算重了。去重要用二维数据结构两个log就TLE了。

正难则反。考虑所有的点对。C(n,2)

对于x到z路径上都比x,z小的去掉,都比x、z大的去掉。就可以了。

具体来说,维护一个树状数组,

以去掉路径上都比x、z小的为例:

之前访问的作为x,如果x到根节点的路径上(包括根)最大值(不存在就是一个任意问题了)小于x,把x位置++

dfs统计,对于z,如果G到z路径上的最大值mx小于z,统计query(z-1)-query(mx)

表示得到编号在mx+1到z-1的x,且x到根路径上的最大值小于x的x数量。

就可以去掉这部分。

当然,因为G儿子的循环顺序,必须正序循环一遍,再倒序循环一遍。当前都作为z,之前的作为x,一定不会漏

另一个都比x,z大的同理。

而且之后统计路径上比x、z都大的情况不会算重。

小细节:

1.C(n,2)会爆int

2.子树的sz不是开始统计的sz,递归之前,必须从新的根即重心G再dfs统计sz

3.点分治一定要时刻控制:if(vis[e[i].to]) continue 否则T得飞起,WA的痛快。

4.发现,对于每条边的两端点对,会被减掉两次。

所以,ans开始还要加上(n-1)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100000+5;
const int inf=0x3f3f3f3f;
ll n;
int rt,nowsz;
bool vis[N];
int mxsz[N],sz[N];
int f[N];
void add(int x,int c){//树状数组 for(;x<=n;x+=x&(-x)) f[x]+=c;
}
int query(int x){int ret=0;for(;x;x-=x&(-x)) ret+=f[x];return ret;
}
int sta[N],top;
int mxid[N],miid[N];//路径上编号最小值,最大值
ll ans;
struct node{int nxt,to;int pre;
}e[2*N];
int hd[N],cnt;
int las[N];
void con(int x,int y){//注意建立双向邻接表,便于反过来dfs if(hd[x]&&e[hd[x]].nxt==0) las[x]=hd[x];e[++cnt].nxt=hd[x];e[hd[x]].pre=cnt;e[cnt].to=y;hd[x]=cnt;
}
void dfs0(int x,int fa){//dfs0找根 sta[++top]=x;mxid[x]=0;mxsz[x]=0;miid[x]=0;sz[x]=1;for(int i=hd[x];i;i=e[i].nxt){int y=e[i].to;if(y==fa) continue;if(vis[y]) continue;dfs0(y,x);sz[x]+=sz[y];mxsz[x]=max(mxsz[x],sz[y]);}if(mxsz[x]<=nowsz/2&&(nowsz-sz[x])<=nowsz/2) rt=x;
}
void fsz(int x,int fa){//找完rt更新sz sz[x]=1;for(int i=hd[x];i;i=e[i].nxt){if(vis[e[i].to]) continue; if(e[i].to!=fa){fsz(e[i].to,x);sz[x]+=sz[e[i].to];}}
}
void dfs1(int x,int mx,int fa){//dfs1统计答案,对于路径上的点都比x,z小的。 mxid[x]=mx;if(mx<x) ans-=(query(x-1)-query(mx));for(int i=hd[x];i;i=e[i].nxt){int y=e[i].to;if(y==fa) continue;if(vis[y]) continue;dfs1(y,max(mx,x),x);}
}
void upda1(int x,int fa){//dfs1之后,更新子树 if(mxid[x]<x) add(x,1);for(int i=hd[x];i;i=e[i].nxt){int y=e[i].to;if(y==fa) continue;if(vis[y]) continue;upda1(y,x);}
}
void srt1(int x,int fa,int mx){//根比较麻烦,单独处理 //if(mx<rt&&x>rt) ans--;if(mx<rt&&x>mx) ans--;for(int i=hd[x];i;i=e[i].nxt){int y=e[i].to;if(y==fa) continue;if(vis[y]) continue;srt1(y,x,max(mx,x));}
}
void dvi1(int in){//点分治1 dfs0(in,0);fsz(rt,0);for(int i=hd[rt];i;i=e[i].nxt){if(vis[e[i].to]) continue;dfs1(e[i].to,rt,rt);upda1(e[i].to,rt);}for(int i=1;i<=top;i++){int x=sta[i];if(x==rt) continue;if(mxid[x]<x) add(x,-1);}for(int i=las[rt];i;i=e[i].pre){//反向再处理一次 if(vis[e[i].to]) continue;dfs1(e[i].to,rt,rt);upda1(e[i].to,rt);}for(int i=hd[rt];i;i=e[i].nxt){if(vis[e[i].to]) continue;srt1(e[i].to,rt,0);}while(top){int x=sta[top--];if(x==rt) continue;if(mxid[x]<x) add(x,-1);}vis[rt]=1;for(int i=hd[rt];i;i=e[i].nxt){int y=e[i].to;if(vis[y]) continue;nowsz=sz[y];dvi1(y);}
}
//以下是x,z路径上点都比较大的,同理
void dfs2(int x,int mi,int fa){miid[x]=mi;if(mi>x) ans-=(query(mi-1)-query(x));for(int i=hd[x];i;i=e[i].nxt){int y=e[i].to;if(y==fa) continue;if(vis[y]) continue;dfs2(y,min(mi,x),x);}
}
void upda2(int x,int fa){if(miid[x]>x) add(x,1);for(int i=hd[x];i;i=e[i].nxt){int y=e[i].to;if(y==fa) continue;if(vis[y]) continue;upda2(y,x);}
}
void srt2(int x,int fa,int mi){if(mi>rt&&x<mi) ans--;for(int i=hd[x];i;i=e[i].nxt){int y=e[i].to;if(y==fa) continue;if(vis[y]) continue;srt2(y,x,min(mi,x));}
}
void dvi2(int in){dfs0(in,0);fsz(rt,0);for(int i=hd[rt];i;i=e[i].nxt){if(vis[e[i].to]) continue;dfs2(e[i].to,rt,rt);upda2(e[i].to,rt);}for(int i=1;i<=top;i++){int x=sta[i];if(x==rt) continue;if(miid[x]>x) add(x,-1);}for(int i=las[rt];i;i=e[i].pre){if(vis[e[i].to]) continue;dfs2(e[i].to,rt,rt);upda2(e[i].to,rt);}for(int i=hd[rt];i;i=e[i].nxt){if(vis[e[i].to]) continue;srt2(e[i].to,rt,inf);}while(top){int x=sta[top--];if(x==rt) continue;if(miid[x]>x) add(x,-1);}vis[rt]=1;for(int i=hd[rt];i;i=e[i].nxt){int y=e[i].to;if(vis[y]) continue;nowsz=sz[y];dvi2(y);}
}
int main(){scanf("%lld",&n);int x,y,z;for(int i=1;i<=n-1;i++){scanf("%d%d%d",&x,&y,&z);con(x,y);con(y,x);}ans=(n-1)*n/2 + (n-1);//warning warning warning!!!nowsz=n;dvi1(1);memset(vis,0,sizeof vis);//解开封锁 top=0;nowsz=n;dvi2(1);printf("%lld",ans);return 0;
}

转载于:https://www.cnblogs.com/Miracevin/p/9633539.html

EOJ 306 树上问题相关推荐

  1. 点分治——树上路径统计

    点分治: 一种分治的方法,一般用于(在不修改情况下),处理两点树上的两点之间的路径的问题. 每次从当前的子图中找到重心,即点分治"点"的含义. 以重心为当前的根节点,查找一切经过重 ...

  2. [BZOJ4033][HAOI2015]树上染色

    4033: [HAOI2015]树上染色 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 2108  Solved: 901 [Submit][Sta ...

  3. Codeforces Round #417:E. FountainsSagheer and Apple Tree(树上博弈)

    Codeforces Round #417:E. FountainsSagheer and Apple Tree(树上博弈) 标签: codeforces 2017-06-02 11:41 29人阅读 ...

  4. POJ - 3417 Network LCA+树上差分

    思路:首先来说,给一个树加一条边肯定要构成一个环,我们假设加了该边后产生的环上的每一条边都累计加一. 假设这条边是a到b,那么其实就是原树a->lca(a,b)到b->lca(a,b)上的 ...

  5. [CTSC2018]混合果汁 二分 套 主席树上二分

    题意: n种果汁,第iii种果汁的美味程度是did_idi​有lil_ili​​升,价格是pip_ipi​升 mmm个询问,要求使用果汁的总量不小于LimLimLim,价格不超过GGG,最大化所用的果 ...

  6. [湖南集训]更为厉害 树上主席树-以树深度为下下标建立主席树

    题意题解: 首先对于树上某个点a来说,假设点b是a的祖先(也就是在a的上面),那么答案很好计算,也就是min(k,dep[a]−1)∗(size[a]−1)min(k,dep[a]-1)*(size[ ...

  7. P4216 [SCOI2015]情报传递 LCA+树上主席树 离线操作

    题意: 给你一棵n个点的树,初始每个位置没有点权 有m次操作 1 x:让一个点从当前时刻开始,每秒操作点权++ 2 x y c:查询一条链中有多少点的点权大于c 其中每秒操作点权++就是指我每操作一次 ...

  8. [51nod] 1766树上的最远点对 树的直径 树剖LCA+ST表静态查询

    题意: 给你一棵带权树,q次查询,每次给出两个区间,[l1,r1][l2,r2][l_1,r_1] [l_2,r_2][l1​,r1​][l2​,r2​]从这两个区间中分别选择两个数字,使得这两个点的 ...

  9. Codeforces Round #406 (Div. 1) B. Legacy(线段树上优化建图)

    题意: 题解: 如何处理从v往[l−r][l-r][l−r]每个点进行连边? 如果暴力连边就有n2n^2n2条边,无法承受. 考虑如何优化区间建图. 线段树,它可以在区间询问/修改的时候,把一个区间分 ...

  10. POJ 2828-Buy Tickets(线段树上二分)

    题意: 有N个人排队,每一个人都有一个val来对应,每一个后来人都会插入当前队伍的某一个位置pos.要求把队伍最后的状态输出. 题解: 这题我们会发现,如果正着操作,每次加入会带来相对位置的变化,不太 ...

最新文章

  1. java计算代码执行时间
  2. Ubuntu常用翻译工具——星际译王StarDict
  3. Apache Kafka-消费端消费重试和死信队列
  4. 【SA 认证课】来啦 这次陪你过双 11
  5. 程序员面试100题之三:不用+、-、×、÷数字运算符做加法
  6. 单片机ADC采样算法----加权递推平均滤波法
  7. 705. 设计哈希集合
  8. python_知识点_字符串+数字+列表
  9. 最新的SAS SID 2023可用至2023年1月SAS 9.4 SID续订更新sas sid 2022服务器通用版server
  10. 树莓派 串口如何使用 以及树莓派引脚对照表
  11. android wear2.0 更新,又一批手表获得Android Wear 2.0更新
  12. TUP首期主题论坛报道:中小型开发商移动开发的生存之道
  13. 编写函数(fun),通过函数调用,输入存款金额和存款年限,计算到期总金额和利息。
  14. 小白如何学3D建模?从零开始变大神
  15. android 观察者模式
  16. C#合并单元格,AddMergedRegion
  17. oracle 视图带变量条件,oracle视图(带参数)
  18. 部署Nextcloud私有云
  19. 3GPP协议中定义的band/带宽范围
  20. 黑马程序员——JAVA笔记——单例设计模式

热门文章

  1. 白板推导系列Pytorch-朴素贝叶斯
  2. 合上More Exceptional C++的瞬间
  3. 深入搜索引擎——海量信息的压缩、索引和查询
  4. 反思设计——从大师身上反思
  5. 4.3. tensorflow2实现相关分析概述与简单相关系数计算——python实战
  6. module 'tensorflow.python.keras.backend' has no attribute 'get_graph'
  7. 自动拷贝远程服务器文件夹,怎么从远程服务器上拷贝文件夹
  8. opengl 矩阵投影代码 shade_LookAt、Viewport、Perspective矩阵
  9. 编译android7.0出现的错误ninja: build stopped: subcommand failed.
  10. 《JavaScript 高级程序设计》第三章:基本概念