题解:

因为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. jsp用el表达式获取后台传来的值,或者获取session中的值

    <script type="text/javascript"> var usernameC = ${sessionScope.SESSION_USER_PROFILE. ...

  2. DAS、SAN和NAS三种存储方式

    DAS存储 DAS存储在我们生活中是非常常见的,尤其是在中小企业应用中,DAS是最主要的应用模式,存储系统被直连到应用的服务器中,在中小企业中,许多的数据应用是必须安装在直连的DAS存储器上. DAS ...

  3. 【原】Java学习笔记003 - 数据类型

    package cn.temptation; public class Sample01 { public static void main(String[] args) { System.out.p ...

  4. c/c++ linux epoll系列2 利用epoll_wait查看是否可以送信

    linux epoll系列2 利用epoll_wait查看是否可以送信 write函数本来是非阻塞函数,但是当缓存区被写满后,再往缓存区里写的时候,就必须等待缓存区再次变成可写,所以这是write就变 ...

  5. PE文件格式对定位病毒特征码的作用

    本文主要从杀毒软件查杀病毒的原理出发,分析PE文件格式在杀毒软件定位病毒特征码中的作用.杀毒软件通过快速准确定位病毒特征码,对伪装,隐藏,变种病毒进行查杀. 一.杀毒软件查杀病毒的原理概述 对于操作系 ...

  6. SQLServer之创建存储过程

    创建存储过程注意事项 在 SQL Server. Azure SQL Database.Azure SQL 数据仓库和并行数据库中创建 Transact-SQL 或公共语言运行时 (CLR) 存储过程 ...

  7. 跨域 - 自定义 jsonp实现跨域

    问题:在现代浏览器中默认是不允许跨域. 办法:通过jsonp实现跨域   在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的.但是,在页面上引入不同域上的js脚本文件却是 ...

  8. 报错:[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop bei

    项目中遇到父组件传值 activeIndex <Tabs :tabs="tabs" :activeIndex="activeIndex" >< ...

  9. fastjson List转JSONArray以及JSONArray转List

    1.fastjson  List转JSONArrayList<T> list = new ArrayList<T>();JSONArray array= JSONArray.p ...

  10. ESP8266当中继

    WiFi推原理(转) http://jb.tongxinmao.com/Article/Detail/id/412 https://www.anywlan.com/thread-409913-1-1. ...