[八分之三的男人] POJ - 1741 点分治 && 点分治笔记
题意:给出一棵带边权树,询问有多少点对的距离小于等于\(k\)
本题解参考lyd的算法竞赛进阶指南,讲解的十分清晰,比网上那些讲的乱七八糟的好多了
不过写起来还是困难重重(史诗巨作
打完多校更详细做法
对于所有路径,以某个节点u来看分为两种情况
1.经过u的路径
2.不经过u的路径
能对答案有贡献的肯定是1类型,对2我们处理完1后递归求解
于是条件变为
以u为根的树中,其联通块中的点对符合
距离u之和小于等于k ①
且位于各不同的u的子树(u单独处理) ②
不妨先维护①再维护②
每一次选定根u后预处理联通块内的点相对于u的距离dis,以及归属于哪个子树belong,
并且把它们放入数组vec中,清空存在的cnt(cnt[t]表示归属于子树t的点的个数有多少个,其中cnt[u]设为单独节点并不包含其它子树)
处理完后对vec数组的节点按dis排序,这样做的目的是为了\(O(n)\)处理贡献
贡献该怎么算?如果在vec中设两个指针L,R,满足最小的L和最大的R符合\(dis[vec[L]]+dis[vec[R]]≤k\),这就满足了①条件
那么②呢?显然是在统计完\([L+1,R]\)范围内的\(cnt[belong[vec[i]]]\)后求\(R-(L+1)-1-cnt[belong[vec[L]]]\)
这个时候vec对dis排序就显得很有用,k是恒定的,随着L指针的右移,R指针也一定是左移,直到\(L=R\)就表明以后无论怎么移动都不会符合条件①了
在指针移动的过程中我们顺便完成了对cnt的统计更新,所以只需L从头到尾扫一遍,每个元素至多被指针遍历4次,整个操作是\(O(n)\)的(虽然排序是nlogn的
由此我们完成了对u有关的第一个情况的所有路径统计
第二个情况只需把u删去(block标记,删去也意味着所有经过u的路径不会再遍历,因为不会再有任何贡献了)递归接下来的子树即可
为了避免单链型的\(O(n)\)次递归,我们选择将每一个子树的重心作为根进行处理,以达到\(O(logn)\)的最坏情况
具体的先dfs一下更新子树大小得出联通块大小V再不断比对删除节点后的最大联通块的最小值就能找到了
总而言之我们在\(O(nlog^2n)\)的时间完成了传说中的点分治啦
八分之三的男人达成√
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
//#include<unordered_map>
#define rep(i,j,k) for(register int i=j;i<=k;i++)
#define rrep(i,j,k) for(register int i=j;i>=k;i--)
#define erep(i,u) for(register int i=head[u];~i;i=nxt[i])
#define print(a) printf("%lld",(ll)a)
#define println(a) printf("%lld\n",(ll)a)
using namespace std;
const int MAXN = 1e5+11;
const int INF = 0x3f3f3f3f;
const double EPS = 1e-7;
typedef long long ll;
ll read(){
ll x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,k;
bool vis[MAXN],block[MAXN];
int dis[MAXN],belong[MAXN],cnt[MAXN],sz[MAXN];
int to[MAXN<<1],nxt[MAXN<<1],cost[MAXN<<1],head[MAXN],tot;
vector<int> vec;
void init(){
memset(head,-1,sizeof head);
memset(vis,0,sizeof vis);
memset(block,0,sizeof block);
tot=0;
}
void add(int u,int v,int w){
to[tot]=v;cost[tot]=w;
nxt[tot]=head[u];head[u]=tot++;
}
int mxsize,mxson,V;//V==N // each V of subtrees can obtain using sz
int getroot(int u,int fa){
sz[u]=1;
int tmp=0;//max size of subtrees while u deleted
erep(i,u){
int v=to[i];
if(v==fa) continue;
if(block[v]) continue;
getroot(v,u);
sz[u]+=sz[v];
tmp=max(tmp,sz[v]);
}
tmp=max(tmp,V-sz[u]);
if(tmp<mxsize){
mxsize=tmp;
mxson=u;// top and down compared
}
return mxson;
}
bool cmp(int a,int b){return dis[a]<dis[b];}
void prepare(int u,int fa,int d,int son,int rt){
dis[u]=d; vec.push_back(u);
belong[u]=son; cnt[son]=0;
erep(i,u){
int v=to[i],w=cost[i];
if(v==fa) continue;
if(block[v]) continue;
prepare(v,u,d+w,son==rt?v:son,rt);
}
}
int getV(int u,int fa){
sz[u]=1;
erep(i,u){
int v=to[i];
if(v==fa) continue;
if(block[v]) continue;
getV(v,u);
sz[u]+=sz[v];
}
return sz[u];
}
ll solve(int u,int fa){
if(vis[u]||u<1) return 0;
vis[u]=1;
ll ans=0;
vec.clear();
prepare(u,fa,0,u,u);
sort(vec.begin(),vec.end(),cmp);
int L,R=vec.size();R--;
for(int i=0;i<vec.size();i++) cnt[belong[vec[i]]]++;
bool flag=0;
for(int i=0;i+1<(int)vec.size();i++){//enum L
L=i;cnt[belong[vec[L]]]--;
while(dis[vec[L]]+dis[vec[R]]>k){
if(L>=R) break;
cnt[belong[vec[R]]]--;
R--;
}
if(L>=R) break;
ans+=(ll)R-L-cnt[belong[vec[L]]];
}
while(L<vec.size()) cnt[belong[vec[L++]]]=0;
block[u]=1;
erep(i,u){
int v=to[i];
if(v==fa) continue;
if(block[v]) continue;
V=getV(v,u);
mxson=v,mxsize=INF;
int rt=getroot(v,u);
ans+=solve(rt,u);
}
return ans;
}
int main(){
while(cin>>n>>k){
if(n==0&&k==0) break;
init();
rep(i,1,n-1){
int u=read();
int v=read();
int w=read();
add(u,v,w);
add(v,u,w);
}
mxsize=INF;mxson=1;V=n;
int rt=getroot(1,-1);
println(solve(rt,-1));
}
return 0;
}
[八分之三的男人] POJ - 1741 点分治 && 点分治笔记的更多相关文章
- poj 1741 树的点分治(入门)
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 18205 Accepted: 5951 Description ...
- POJ 1741 Tree 树上点分治
题目链接:http://poj.org/problem?id=1741 题意: 给定一棵包含$n$个点的带边权树,求距离小于等于K的点对数量 题解: 显然,枚举所有点的子树可以获得答案,但是朴素发$O ...
- POJ 1741 Tree 树的分治
原题链接:http://poj.org/problem?id=1741 题意: 给你棵树,询问有多少点对,使得这条路径上的权值和小于K 题解: 就..大约就是树的分治 代码: #include< ...
- POJ 1741 Tree【树分治】
第一次接触树分治,看了论文又照挑战上抄的代码,也就理解到这个层次了.. 以后做题中再慢慢体会学习. 题目链接: http://poj.org/problem?id=1741 题意: 给定树和树边的权重 ...
- poj 1741 Tree(点分治)
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 15548 Accepted: 5054 Description ...
- POJ 1741 Tree (树分治入门)
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 8554 Accepted: 2545 Description ...
- POJ 1741 Tree (点分治)
Tree Time Limit: 1000MS Memory ...
- POJ 1741 树的点分治
题目大意: 树上找到有多少条路径的边权值和>=k 这里在树上进行点分治,需要找到重心保证自己的不会出现过于长的链来降低复杂度 #include <cstdio> #include & ...
- POJ 1741 树上的点分治
题目大意: 找到树上点对间距离不大于K的点对数 这是一道简单的练习点分治的题,注意的是为了防止点分治时出现最后分治出来一颗子树为一条直线,所以用递归的方法求出最合适的root点 #include &l ...
随机推荐
- Python爬虫实战三之实现山东大学无线网络掉线自动重连
综述 最近山大软件园校区QLSC_STU无线网掉线掉的厉害,连上之后平均十分钟左右掉线一次,很是让人心烦,还能不能愉快地上自习了?能忍吗?反正我是不能忍了,嗯,自己动手,丰衣足食!写个程序解决掉它! ...
- DESC和 ACS
用 DESC 表示按倒序排序(即:从大到小排序)用 ACS 表示按正序排序(即:从小到大排序)
- (二)ASP.NET中JavaScript的中英文(多语言)实现方案(二)
在ASP.NET中JavaScript的中英文(多语言)实现方案中简单的介绍了js实现多语言的一种方案.下面将要讲述另外一种方法,尽管很相似,但是有些地方也是需要细细琢磨的,不说了,先看看. 在Lan ...
- 解决svn:E155037错误(另附查看.db文件的工具)
今天使用svn提交代码的时候出问题了,Error:svn: E155037.....Previous operation has not finished; run 'cleanup' if it w ...
- mysql中四种存储引擎的区别和选择
前言 数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建.查询.更新和删除数据.不同的存储引擎提供不同的存储机制.索引技巧.锁定水平等功能,使用不同的存储引擎,还可以 ...
- MongoDB整理笔记のID自增长
以下是官网原文地址: http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/ 概要 MongoDB 的_i ...
- python中用ElementTree.iterparse()读取xml文件中的多层节点
我在使用Python解析比较大型的xml文件时,为了提高效率,决定使用iterparse()方法,但是发现根据网上的例子:每次if event == 'end':之后elem.clear()或者是每次 ...
- 加载log文件
procedure Jslog(szLogText:string);var pMyFile : TextFile;begin Assignfile(pMyFile, 'log.txt'); if Fi ...
- C#中Attribute/特性的使用
类似Java的注解/Annotation 特性是用于在运行时传递程序中各种元素(比如类.方法.结构.枚举.组件等)的行为信息的声明性标签,这个标签可以有多个.您可以通过使用特性向程序添加声明性信息.一 ...
- python index()函数
python内置index()函数 index() 方法检测字符串中是否包含子字符串 str ,如果指定 beg(开始) 和 end(结束) 范围,则检查是否包含在指定范围内,该方法与 python ...