题目:https://www.luogu.org/problemnew/show/P4178

点分治。如果把每次的 dis 和 K-dis 都离散化,用树状数组找,是O(n*logn*logn),会T7个点。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=4e4+;
int n,hd[N],xnt,to[N<<],nxt[N<<],w[N<<],f[N<<],siz[N],ans,mn,rt;
ll dis[N],tis[N],tp[N<<],tnt,K;
bool vis[N],sj[N];
void add(int x,int y,ll z)
{
to[++xnt]=y;nxt[xnt]=hd[x];w[xnt]=z;hd[x]=xnt;
to[++xnt]=x;nxt[xnt]=hd[y];w[xnt]=z;hd[y]=xnt;
}
void getrt(int cr,int fa,int s)
{
siz[cr]=;int mx=;
for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=fa)
{
getrt(v,cr,s);siz[cr]+=siz[v];mx=max(mx,siz[v]);
}
mx=max(mx,s-siz[cr]);
if(mx<mn)mn=mx,rt=cr;
}
void add(int x){for(;x<=tnt;x+=(x&-x))f[x]++;}
int query(int x){int ret=;for(;x;x-=(x&-x))ret+=f[x];return ret;}
void dfs(int cr,int fa,ll lj)
{
dis[cr]=lj;sj[cr]=;
for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=fa)
dfs(v,cr,lj+w[i]);
}
int calc(int cr,ll w)
{
memset(sj,,sizeof sj);tnt=;dfs(cr,,w);
for(int i=;i<=n;i++) if(sj[i]&&dis[i]<=K)
{
tis[i]=K-dis[i];tp[++tnt]=dis[i];tp[++tnt]=tis[i];
// printf("dis[%d]=%lld tis[%d]=%lld\n",i,dis[i],i,tis[i]);
}
sort(tp+,tp+tnt+);tnt=unique(tp+,tp+tnt+)-tp-;
int ret=;
for(int i=;i<=n;i++) if(sj[i]&&dis[i]<=K)
{
dis[i]=lower_bound(tp+,tp+tnt+,dis[i])-tp;
tis[i]=lower_bound(tp+,tp+tnt+,tis[i])-tp;
// printf("dis[%d]=%lld tis[%d]=%lld\n",i,dis[i],i,tis[i]);
ret+=query(tis[i]);add(dis[i]);
}
memset(f,,sizeof f);
return ret;
}
void solve(int cr,int s)
{
// printf("rt=%d\n",cr);
vis[cr]=;
ans+=calc(cr,);
// printf("cr=%d ans=%d\n",cr,ans);
for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]])
{
ans-=calc(v,w[i]);
int ts=(siz[cr]>siz[v]?siz[v]:s-siz[cr]);//-siz[cr]!!!
mn=N;getrt(v,,ts);solve(rt,ts);
}
}
int main()
{
scanf("%d",&n);int x,y;ll z;
for(int i=;i<n;i++)
{
scanf("%d%d%lld",&x,&y,&z);add(x,y,z);
}
scanf("%lld",&K);
mn=N;getrt(,,n);solve(rt,n);
printf("%d\n",ans);
return ;
}

应当排序后枚举两个指针。(代码中两种方法时间一样)

如果把 ts=s-siz[cr] 写成 ts=s-siz[v] ,就会T7个点(?)!!!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=4e4+;
int n,hd[N],xnt,to[N<<],nxt[N<<],w[N<<],siz[N],mn,rt,sta[N],top,K,ans;
bool vis[N];
void add(int x,int y,int z)
{
to[++xnt]=y;nxt[xnt]=hd[x];w[xnt]=z;hd[x]=xnt;
to[++xnt]=x;nxt[xnt]=hd[y];w[xnt]=z;hd[y]=xnt;
}
void getrt(int cr,int fa,int s)
{
siz[cr]=;int mx=;
for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=fa)
{
getrt(v,cr,s);siz[cr]+=siz[v];mx=max(mx,siz[v]);
}
mx=max(mx,s-siz[cr]);
if(mx<mn)mn=mx,rt=cr;
}
void dfs(int cr,int fa,int lj)
{
sta[++top]=lj;
for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=fa)
dfs(v,cr,lj+w[i]);
}
int calc(int cr,int w)
{
int ret=;dfs(cr,,w);
// l=1;r=0;
// sort(sta+l,sta+r+1);
// while(l<=r)
// if(sta[l]+sta[r]<=K)ret+=r-l,l++;
// else r--;
sort(sta+,sta+top+);int p=top;
for(int i=;i<=top;i++)
{
while(sta[p]+sta[i]>K&&p)p--;if(!p)break;
ret+=p-(p>=i);
}
top=;
// printf("cr=%d ret=%d\n",cr,ret);
return ret>>;
}
void solve(int cr,int s)
{
// printf("rt=%d\n",cr);
vis[cr]=;
ans+=calc(cr,);
// printf("cr=%d ans=%d\n",cr,ans);
for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]])
{
ans-=calc(v,w[i]);
int ts=(siz[cr]>siz[v]?siz[v]:s-siz[cr]);//s-siz[cr]!!!
mn=N;getrt(v,,ts);solve(rt,ts);
}
}
int main()
{
scanf("%d",&n);
for(int i=,x,y,z;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);add(x,y,z);
}
scanf("%d",&K);
mn=N;getrt(,,n);solve(rt,n);
printf("%d\n",ans);
return ;
}

洛谷 4178 Tree——点分治的更多相关文章

  1. 洛谷P4178 Tree (点分治)

    题目描述 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K 输入输出格式 输入格式:   N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下 ...

  2. 解题:洛谷4178 Tree

    题面 重(新)学点分治中...... 普通的点分治一般这几步: 1.找重心 2.从重心开始DFS,得到信息 3.统计经过重心的路径 4.分别分治几棵子树,继续这个过程 然后是常见的(制杖的我的)一些疑 ...

  3. [洛谷P4178] Tree (点分治模板)

    题目略了吧,就是一棵树上有多少个点对之间的距离 \(\leq k\) \(n \leq 40000\) 算法 首先有一个 \(O(n^2)\) 的做法,枚举每一个点为起点,\(dfs\) 一遍可知其它 ...

  4. 洛谷 P4178 Tree —— 点分治

    题目:https://www.luogu.org/problemnew/show/P4178 这道题要把 dep( dis? ) 加入一个 tmp 数组里,排序,计算点对,复杂度很美: 没有写 sor ...

  5. 点分治模板(洛谷P4178 Tree)(树分治,树的重心,容斥原理)

    推荐YCB的总结 推荐你谷ysn等巨佬的详细题解 大致流程-- dfs求出当前树的重心 对当前树内经过重心的路径统计答案(一条路径由两条由重心到其它点的子路径合并而成) 容斥减去不合法情况(两条子路径 ...

  6. Poj1741/洛谷P4718 Tree(点分治)

    题面 有多组数据:Poj 无多组数据:洛谷 题解 点分治板子题,\(calc\)的时候搞一个\(two\ pointers\)扫一下统计答案就行了. #include <cmath> #i ...

  7. 洛谷P3810 陌上花开 CDQ分治(三维偏序)

    好,这是一道三维偏序的模板题 当然没那么简单..... 首先谴责洛谷一下:可怜的陌上花开的题面被无情的消灭了: 这么好听的名字#(滑稽) 那么我们看了题面后就发现:这就是一个三维偏序.只不过ans不加 ...

  8. [洛谷P3806] [模板] 点分治1

    洛谷 P3806 传送门 这个点分治都不用减掉子树里的了,直接搞就行了. 注意第63行 if(qu[k]>=buf[j]) 不能不写,也不能写成>. 因为这个WA了半天...... 如果m ...

  9. POJ1471 Tree/洛谷P4178 Tree

    Tree P4178 Tree 点分治板子. 点分治就是直接找树的重心进行暴力计算,每次树的深度不会超过子树深度的\(\frac{1}{2}\),计算完就消除影响,找下一个重心. 所以伪代码: voi ...

随机推荐

  1. 几种动态调用js函数方案的性能比较

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 九度OJ 1191:矩阵最大值 (矩阵计算)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:2361 解决:1179 题目描述: 编写一个程序输入一个mXn的矩阵存储并输出,并且求出每行的最大值和每行的总和. 要求把每行总和放入每行最 ...

  3. 九度OJ 1002:Grading

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:18410 解决:4753 题目描述: Grading hundreds of thousands of Graduate Entrance ...

  4. S-形函数广泛应用于ANN 的激活函数

    Logistic function hyperbolic tangent   arctangent function   Gudermannian function   Error function ...

  5. C++, Java和C#的编译、链接过程解析

    总是感觉java是解释性语言,转载下一篇感觉写的容易理解的文章 转自 http://www.cnblogs.com/rush/p/3155665.html 1.1.1 摘要 我们知道计算机不能直接理解 ...

  6. 【题解】Cutting Game

    [题解]Cutting Game vjudge 谈谈对\(sg\)函数的理解? 浅谈公平组合游戏IGC //@winlere #include<cstring> #include<c ...

  7. PHPUnit学习记录

    今天是2017-1-17号,昨晚收到邮件,被view code之后,基本全部需要重构,其实我写得php代码里面完全是东拼西凑的代码,自己都不知道什么意思,今天被要求学习PHPUnit了 ------- ...

  8. Android LockScreen (锁屏弹窗)

    在要弹窗的Activity需要进行以下设置,才可以在锁屏状态下弹窗 @Override protected void onCreate(Bundle savedInstanceState) { fin ...

  9. Java之线程池(二)

    关于线程和线程池的学习,我们可以从以下几个方面入手: 第一,什么是线程,线程和进程的区别是什么 第二,线程中的基本概念,线程的生命周期 第三,单线程和多线程 第四,线程池的原理解析 第五,常见的几种线 ...

  10. python3保存一个网页

    import requests res = requests.get("http://www.baidu.com") savefile = open("baidu.htm ...