[poj1741]Tree(点分治+容斥原理)

题意:求树中点对距离<=k的无序点对个数。

解题关键:树上点分治,这个分治并没有传统分治的合并过程,只是分成各个小问题,并将各个小问题的答案相加即可,也就是每层的复杂度并不在合并的过程,是在每层的处理过程。

此题维护的是树上路径,考虑点分治。

点分治的模板题,首先设点x到当前子树跟root的距离为,则满足${d_x} + {d_y} \le k$可以加进答案,但是注意如果x,y在同一棵子树中,就要删去对答案的贡献,因为x,y会在其所在的子树中在计算一次。同一棵子树中不必考虑是否在其同一棵子树中的问题,因为无论是否在他的同一棵子树,都会对他的父节点产生影响。而这些影响都是无意义的。

注意无根树转有根树的过程,需要选取树的重心防止复杂度从$O(n{\log ^2}n)$退化为$O({n^2})$

复杂度:$O(n{\log ^2}n)$

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<cstdlib>
  5 #include<iostream>
  6 #include<cmath>
  7 #define inf 0x3f3f3f3f
  8 #define maxn 10004
  9 using namespace std;
 10 typedef long long ll;
 11 int head[maxn],cnt,n,k,ans,size,s[maxn],f[maxn],root,depth[maxn],num;//vis代表整体的访问情况,每个dfs不应该只用vis来存储
 12 bool vis[maxn];
 13 struct edge{
 14     int to,w,nxt;
 15 }e[maxn<<1];
 16 void add_edge(int u,int v,int w){
 17     e[cnt].to=v;
 18     e[cnt].w=w;
 19     e[cnt].nxt=head[u];
 20     head[u]=cnt++;
 21 }
 22
 23 inline int read(){
 24     char k=0;char ls;ls=getchar();for(;ls<‘0‘||ls>‘9‘;k=ls,ls=getchar());
 25     int x=0;for(;ls>=‘0‘&&ls<=‘9‘;ls=getchar())x=(x<<3)+(x<<1)+ls-‘0‘;
 26     if(k==‘-‘)x=0-x;return x;
 27 }
 28
 29 void get_root(int u,int fa){//get_root会用到size
 30     s[u]=1;f[u]=0;//f是dp数组
 31     for(int i=head[u];i!=-1;i=e[i].nxt){
 32         int v=e[i].to;
 33         if(v==fa||vis[v]) continue;
 34         get_root(v,u);
 35         s[u]+=s[v];
 36         f[u]=max(f[u],s[v]);
 37     }
 38     f[u]=max(f[u],size-f[u]);
 39     root=f[root]>f[u]?u:root;
 40 }
 41
 42 void get_depth_size(int u,int fa,int dis){//同时获取size和depth
 43     depth[num++]=dis;
 44     s[u]=1;
 45     for(int i=head[u];i!=-1;i=e[i].nxt){
 46         int v=e[i].to;
 47         if(v==fa||vis[v]) continue;
 48         get_depth_size(v,u,dis+e[i].w);
 49         s[u]+=s[v];
 50     }
 51 }
 52
 53 int calc(int u,int fa,int w){
 54     num=0;
 55     get_depth_size(u,fa,w);
 56     sort(depth,depth+num);
 57     int ret=0;
 58     for(int l=0,r=num-1;l<r;){
 59         if(depth[l]+depth[r]<=k) ret+=r-l++;
 60         else r--;
 61     }
 62     return ret;
 63 }
 64
 65 void work(int u){
 66     vis[u]=true;
 67     ans+=calc(u,-1,0);
 68     for(int i=head[u];i!=-1;i=e[i].nxt){
 69         int v=e[i].to;
 70         if(vis[v]) continue;
 71         ans-=calc(v,u,e[i].w);
 72         size=s[v],root=0;
 73         get_root(v,u);
 74         work(root);
 75     }
 76 }
 77
 78 void init(){
 79     memset(vis,false, sizeof vis);
 80     memset(head,-1,sizeof head);
 81     ans=cnt=0;
 82 }
 83
 84
 85 int main(){
 86     int a,b,c;
 87     f[0]=inf;
 88     while(scanf("%d%d",&n,&k)&&(n||k)){
 89         init();
 90         for(int i=0;i<n-1;i++){
 91             a=read(),b=read(),c=read();
 92             add_edge(a,b,c);
 93             add_edge(b,a,c);
 94         }
 95         size=n,root=0;
 96         get_root(1,-1);
 97         work(root);
 98         printf("%d\n",ans);
 99     }
100     return 0;
101
102 }
时间: 2024-11-06 23:32:32

[poj1741]Tree(点分治+容斥原理)的相关文章

POJ1741:Tree——点分治

题面 POJ1741 解析  刚学了点分治,练一练模版题 过程就不多说了,主要说说细节 在每次查询下一棵子树时, 传进去的整棵子树大小是上一次的$siz$, 这个数据其实是错的, 但好像并不影响时间复杂度, 这样的话找重心就必须找最大子树最小的点了,否则会错.因此需要存一个当前最大子树最小的点的最大子树的大小, 以及当前的重心, 每次找重心之前,前者要赋为$inf$ 在每次统计以当前点为$lca$的链的答案时, 要先把这个重心加入数组中, 再按$dis$排序, 然后用双指针法或是其他可行的方法统

poj1741 Tree 点分治

入门题,算是对树分治有了初步的理解吧. #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<vector> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using nam

[bzoj1468][poj1741]Tree[点分治]

可以说是点分治第一题,之前那道的点分治只是模模糊糊,做完这道题感觉清楚了很多,点分治可以理解为每次树的重心(这样会把数分为若干棵子树,子树大小为log级别),然后统计包含重心的整个子树的值减去各个子树的值,这样算出的就是与这个重心有关的情况的答案,比如这道题,求路径,那么就考虑在重心所在的子树中所有的路径减去不过重心的路径就是过重心的路径了.之前重心没找对...poj时间卡的紧就T了.. 1 #include <iostream> 2 #include <algorithm> 3

[LeetCode] Convert Sorted List to Binary Search Tree(分治)

Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. 方法:为了使BST高度平衡,要找链表中的中值作为当前根节点. /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) :

树分治基础模板以及树的重心(poj1741 tree)

好久没有更新博文了,这里更新一发~~ Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v

POJ-1741 Tree 【点分治】

Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not e

poj1741 Tree(点分治)

Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not e

[poj1741 Tree]树上点分治

题意:给一个N个节点的带权树,求长度小于等于K的路径条数 思路:选取一个点作为根root,假设f(root)是当前树的答案,那么答案来源于两部分: (1)路径不经过root,那么就是完全在子树内,这部分可以递归统计 (2)路径经过root,这部分可以通过容斥原理统计,具体见有关点分治资料... 点分治有个特点,当考虑的问题由根这个子树转为儿子的子树时,可以选取任意点作为新的根,只要它在儿子的子树内,这就使得我们可以通过选取特别的点使得树深度更小,这个点就是树的重心(在程序里面是不断找子树的重心)

POJ1741 Tree(树分治)

题意: 求树上距离小于等于K的点对有多少个 思路: 每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心 找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K 但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,所以对每颗子树,把子树内部的满足条件的点对减去. /* ******************************************