EOJ 306 树上问题


题解:
因为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的信息,把路径就变成了点。
这个题,静态无修树上统计,就点分治了。
还可以再带一个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 树上问题的更多相关文章
- EOJ Monthly 2018.8 D. Delivery Service-树上差分(边权/边覆盖)(边权转点权)(模板题)
D. Delivery Service 单测试点时限: 2.5 秒 内存限制: 512 MB EOJ Delivery Service Company handles a massive amount ...
- 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 ...
- BZOJ 3784: 树上的路径
Description 问一棵树上前 \(k\) 大路径的边权. Sol 边分治. 非常感谢数据没有菊花图. 为了写写边分治试试然后就开了这道题. 边分治非常好想,选一条重边,分成两部分,然后分别求最 ...
- HDU 2376 树形dp|树上任意两点距离和的平均值
原题:http://acm.hdu.edu.cn/showproblem.php?pid=2376 经典问题,求的是树上任意两点和的平均值. 这里我们不能枚举点,这样n^2的复杂度.我们可以枚举每一条 ...
- LCA + 树状数组 + 树上RMQ
题目链接:http://poj.org/problem?id=2763 思路:首先求出树上dfs序列,并且标记树上每个节点开始遍历以及最后回溯遍历到的时间戳,由于需要修改树上的某两个节点之间的权值,如 ...
- HDU 2545 树上战争 (并查集+YY)
题意:给一棵树,如果树上的某个节点被某个人占据,则它的所有儿子都被占据,lxh和pfz初始时分别站在两个节点上,lxh总是先移动 ,谁当前所在的点被另一个人占据,他就输了比赛,问谁能获胜 比较有意思的 ...
- poj1155 TELE (树上的背包)
题目链接:http://poj.org/problem?id=1155 题意:给定一棵树,1为根结点表示电视台,有m个叶子节点表示客户,有n-m-1个中间节点表示中转站,每条树边有权值.现在要在电视台 ...
- Codevs 2370 小机房的树 LCA 树上倍增
题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子, ...
- Subway Icon Set – 306个像素完美的特制图标
这个图标集是306个优化的像素完美,精雕细琢的图标.为这些设备进行了优化:iOS.Windows Phone.Windows 8 and BlackBerry 10,提供 PNG, SVG, XALM ...
随机推荐
- vue安装和使用
首先这里记录的是基于安装node.js 的npm安装vue 如果你不是用的node与npm 那就不必往下看了 1.安装node.js这个不多说 百度有很多 2.安装webpack 全局安装we ...
- SqlServer sa 用户登录失败的解决方法
一.控制面板->服务->MS SQL SERVER->登录-->本地系统帐户-->重新启动MS SQL SERVER用windows验证登陆查询分析器-->执行 s ...
- 使用cmd查看电脑连接过的wifi密码(一)
有时总会忘记之前的wifi密码,这时候就需要查看wifi密码: 这个我就写了一个bat文件,方便下次使用,这里我添加了中文和英文的判断,主要是语言不同输出的信息不用,不说了直接上代码: @echo o ...
- linux文件行首行尾添加或替换
sed -i 's/\(^.*\)/http:\/\/www.blutmagie.de\/img\/flags\//g' cc.txt sed -i 's/\($\)/.gif/g' cc.txt
- 从0开始的Python学习001快速上手手册
假设大家已经安装好python的环境了. Windows检查是否可以运行python脚本 Ctrl+R 输入 cmd 在命令行中输入python 如果出现下面结果,我们就可以开始python的学习了. ...
- User Agent 大全
一.基础知识篇: Http Header之User-Agent User Agent中文名为用户代理,是Http协议中的一部分,属于头域的组成部分,User Agent也简称UA.它是一个特殊字符串头 ...
- macos 远程 windows 10
使用的工具:Parallels Client 免费,可以从windows store 免费下载: 只有这个最好用,希望能帮到你.和原生的windows 远程桌面功能很像:
- 局部敏感哈希(Locality-Sensitive Hashing, LSH)
本文主要介绍一种用于海量高维数据的近似最近邻快速查找技术——局部敏感哈希(Locality-Sensitive Hashing, LSH),内容包括了LSH的原理.LSH哈希函数集.以及LSH的一些参 ...
- webstorm 的 .后缀名-tab快捷键
if (key) {}//key.if tab if (!key) {}//key.else tab if (key != null) {}//key.notnull tab if (typeof k ...
- 如何使用U盘安装macOS high Sierra?
当你不再只是想升级系统的时候,而是想把系统重装,你可能就会用到如标题所说的方式:使用U盘安装mac系统.所以我们需要做以下几件事情,就可以顺利地重装mac系统: 第一步.在App Store下载最新的 ...