题解:

因为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=+;
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=;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[*N];
int hd[N],cnt;
int las[N];
void con(int x,int y){//注意建立双向邻接表,便于反过来dfs
if(hd[x]&&e[hd[x]].nxt==) 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]=;mxsz[x]=;
miid[x]=;
sz[x]=;
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/&&(nowsz-sz[x])<=nowsz/) rt=x;
}
void fsz(int x,int fa){//找完rt更新sz
sz[x]=;
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-)-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,);
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,);
fsz(rt,);
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=;i<=top;i++){
int x=sta[i];
if(x==rt) continue;
if(mxid[x]<x) add(x,-);
}
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,);
}
while(top){
int x=sta[top--];
if(x==rt) continue;
if(mxid[x]<x) add(x,-);
}
vis[rt]=;
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-)-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,);
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,);
fsz(rt,);
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=;i<=top;i++){
int x=sta[i];
if(x==rt) continue;
if(miid[x]>x) add(x,-);
}
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,-);
}
vis[rt]=;
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=;i<=n-;i++){
scanf("%d%d%d",&x,&y,&z);
con(x,y);con(y,x);
}
ans=(n-)*n/ + (n-);//warning warning warning!!!
nowsz=n;
dvi1(); memset(vis,,sizeof vis);//解开封锁
top=;
nowsz=n;
dvi2();
printf("%lld",ans);
return ;
}

EOJ 306 树上问题的更多相关文章

  1. EOJ Monthly 2018.8 D. Delivery Service-树上差分(边权/边覆盖)(边权转点权)(模板题)

    D. Delivery Service 单测试点时限: 2.5 秒 内存限制: 512 MB EOJ Delivery Service Company handles a massive amount ...

  2. BZOJ 2588: Spoj 10628. Count on a tree [树上主席树]

    2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MBSubmit: 5217  Solved: 1233 ...

  3. BZOJ 3784: 树上的路径

    Description 问一棵树上前 \(k\) 大路径的边权. Sol 边分治. 非常感谢数据没有菊花图. 为了写写边分治试试然后就开了这道题. 边分治非常好想,选一条重边,分成两部分,然后分别求最 ...

  4. HDU 2376 树形dp|树上任意两点距离和的平均值

    原题:http://acm.hdu.edu.cn/showproblem.php?pid=2376 经典问题,求的是树上任意两点和的平均值. 这里我们不能枚举点,这样n^2的复杂度.我们可以枚举每一条 ...

  5. LCA + 树状数组 + 树上RMQ

    题目链接:http://poj.org/problem?id=2763 思路:首先求出树上dfs序列,并且标记树上每个节点开始遍历以及最后回溯遍历到的时间戳,由于需要修改树上的某两个节点之间的权值,如 ...

  6. HDU 2545 树上战争 (并查集+YY)

    题意:给一棵树,如果树上的某个节点被某个人占据,则它的所有儿子都被占据,lxh和pfz初始时分别站在两个节点上,lxh总是先移动 ,谁当前所在的点被另一个人占据,他就输了比赛,问谁能获胜 比较有意思的 ...

  7. poj1155 TELE (树上的背包)

    题目链接:http://poj.org/problem?id=1155 题意:给定一棵树,1为根结点表示电视台,有m个叶子节点表示客户,有n-m-1个中间节点表示中转站,每条树边有权值.现在要在电视台 ...

  8. Codevs 2370 小机房的树 LCA 树上倍增

    题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子, ...

  9. Subway Icon Set – 306个像素完美的特制图标

    这个图标集是306个优化的像素完美,精雕细琢的图标.为这些设备进行了优化:iOS.Windows Phone.Windows 8 and BlackBerry 10,提供 PNG, SVG, XALM ...

随机推荐

  1. vue中input输入框的模糊查询实现

    最近在使用vue写webapp在,一些感觉比较有意思的分享一下. 1:input输入框: <input class="s-search-text" placeholder=& ...

  2. 手持式停车收费管理系统全套案例,支持车牌识别,包含了android版app,微信小程序查询,响应式管理后台,云端大数据存储

    先展示几个app效果图片吧,使用起来非常方便,关联了机器的快捷键操作,操作速度提高了不少,摄像头车牌自动识别,车牌识别无网络情况下离线也可以使用   再来一张后台截图,停车场信息完整显示,今日数据实时 ...

  3. ASP.NET Core 入门教程 10、ASP.NET Core 日志记录(NLog)入门

    一.前言 1.本教程主要内容 ASP.NET Core + 内置日志组件记录控制台日志 ASP.NET Core + NLog 按天记录本地日志 ASP.NET Core + NLog 将日志按自定义 ...

  4. PJSUA2开发文档--第十章 媒体质量(MEDIA QUALITY)

    10 媒体质量(Media Quality) 10.1 音频质量 如果遇到音频质量问题,可尝试以下步骤: 遵循指南:使用pjsystest测试声音设备. 识别声音问题并使用以下步骤进行故障排除:检查声 ...

  5. 启动期间的内存管理之pagging_init初始化分页机制--Linux内存管理(十四)

    1 今日内容(分页机制初始化) 在初始化内存的结点和内存区域之前, 内核先通过pagging_init初始化了内核的分页机制. 在分页机制完成后, 才会开始初始化系统的内存数据结构(包括内存节点数据和 ...

  6. LeetCode算法题-Design HashSet(Java实现)

    这是悦乐书的第298次更新,第317篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第166题(顺位题号是705).不使用任何内建的hash表库设计一个hash集合,应包含 ...

  7. Linux经常用到的命令以及快捷键

    Linux常用命令和快捷键 最近一直在对CentOS系统进行各种体验,为方便自己也方便他人,整理了Linux常用命令及快捷键,不过其实大多和DOS是一样的,只是命令的表达上可能有点儿不一样. Linu ...

  8. ABAP 7.53 中的ABAP SQL(原Open SQL)新特性

    S/4 HANA 1809 已经在上月发布,随之而来的是ABAP 7.53. 本文是更新文档中ABAP SQL的部分的翻译. 本次更新的内容较多,主要内容包括:Open SQL更名为ABAP SQL: ...

  9. python 文件名的操作

    1.文件操作 open()文件句柄:获取文件的路径信息 open() 打开一个文件,获取文件句柄 read() 全部读出出文件中的内容 readline() 读取文件中逐一读取一行数据 readlin ...

  10. 【Python 08】汇率兑换2.0-1(字符串索引)

     1.案例描述 设计一个汇率换算程序,其功能是将人民币转换为美元,或者美元转换为人民币. 增加功能:根据输入判断是人民币还是美元,进行相应的转换计算. 2.案例分析 3.字符串 两个双引号或单引号括起 ...