poj2114 树分治(点分治)

poj1741板子套一套,统计对数的方式改一下,可以在O(n)时间内统计对数

最后不要忘记输出最后的“.”

/*
给定一棵边权树,是否存在一条路径使得其长度为恰好为x
把1741的板子改为求点对之间的距离=k的点对数即可
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define MAXN 10010
using namespace std;
int N,K;
int ans,root,Max;
struct node{
    int v,next,w;
}edge[MAXN<<1];
int head[MAXN],tot;
int size[MAXN];
int maxv[MAXN];
int vis[MAXN];
int dis[MAXN];
int num;
void init(){
    tot=ans=0;
    memset(head,-1,sizeof head);
    memset(vis,0,sizeof vis);
}
void addedge(int u,int v,int w){
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].next=head[u];
    head[u]=tot++;
}
//一次dfs处理子树的大小
void dfssize(int u,int f){
    size[u]=1;
    maxv[u]=0;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==f||vis[v]) continue;
        dfssize(v,u);
        size[u]+=size[v];
        if(size[v]>maxv[u])maxv[u]=size[v];
    }
}
//一次dfs找重心,这里的r不是重心
void dfsroot(int r,int u,int f){
    if(size[r]-size[u]>maxv[u])
        maxv[u]=size[r]-size[u];
    if(maxv[u]<Max) Max=maxv[u],root=u;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==f||vis[v])
            continue;
        dfsroot(r,v,u);
    }
}
//一次dfs求每个点到重心的距离
void dfsdis(int u,int d,int f){
    dis[num++]=d;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v!=f && !vis[v])
            dfsdis(v,d+edge[i].w,u);
    }
}
int calc(int u,int d){
    int ret=0;
    num=0;
    dfsdis(u,d,0);//求每个点到根的距离
    sort(dis,dis+num);
    for (int l=0, r=num-1; l<r; ) {
        if (dis[l] + dis[r] == K) {
            if (dis[l] == dis[r]) {
                ret += (r-l+1)*(r-l)/2; break;
            }
            int i=l, j=r;
            while (dis[i] == dis[l]) i++;
            while (dis[j] == dis[r]) j--;
            ret += (i-l)*(r-j);
            l = i, r = j;
        } else if (dis[l] + dis[r] < K) l++;
        else r--;
    }
    return ret;
}
void dfs(int u){//注意这里的u并不是重心
    Max=N;
    dfssize(u,0);//求每个子树的规模
    dfsroot(u,u,0);//求重心
    ans+=calc(root,0);//求以root为根,dis[u]+dis[v]的点对有多少
    vis[root]=1;//把这个点从点集删掉
    for(int i=head[root];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!vis[v]){
            ans-=calc(v,edge[i].w);
            dfs(v);
        }
    }
}
int main(){
    while(scanf("%d",&N) && N){
        init();
        int v,w;
        for(int i=1;i<=N;i++){
            while(scanf("%d",&v) && v!=0){
                scanf("%d",&w);
                addedge(i,v,w);
                addedge(v,i,w);
            }
        }
        while(scanf("%d",&K) && K!=0){
            memset(vis,0,sizeof vis);
            ans=0;
            dfs(1);
            if(ans) puts("AYE");
            else puts("NAY");
        }
        puts(".");
    }
    return 0;
} 

原文地址:https://www.cnblogs.com/zsben991126/p/9863846.html

时间: 2025-01-18 11:27:10

poj2114 树分治(点分治)的相关文章

hdu_5314_Happy King(树的点分治)

题目链接:hdu_5314_Happy King 题意: 给出一颗n个结点的树,点上有权值: 求点对(x,y)满足x!=y且x到y的路径上最大值与最小值的差<=D: 题解: 还是树的点分治,在统计答案的时候先按到根的最小值排序,然后用最大值减D去找有多少个满足答案. 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 using namespace std; 4 typedef pair<i

bzoj 3435: [Wc2014]紫荆花之恋 替罪羊树维护点分治 &amp;&amp; AC400

3435: [Wc2014]紫荆花之恋 Time Limit: 240 Sec  Memory Limit: 512 MBSubmit: 159  Solved: 40[Submit][Status][Discuss] Description 强 强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来. 仔细看看的话,这个大树实际上是一个带权树.每个时刻它会长出一个新的叶子节点.每个节点上有一个可爱的小精灵,

bzoj 2152: 聪聪可可 树的点分治

2152: 聪聪可可 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 485  Solved: 251[Submit][Status] Description 聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏.他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“

【bzoj3672】[Noi2014]购票 斜率优化+CDQ分治+树的点分治

题目描述 给出一棵以1为根的带边权有根树,对于每个根节点以外的点$v$,如果它与其某个祖先$a$的距离$d$不超过$l_v$,则可以花费$p_vd+q_v$的代价从$v$到$a$.问从每个点到1花费的最小代价(中途可以经停其它点) 输入 第 1 行包含2个非负整数 n,t,分别表示城市的个数和数据类型(其意义将在后面提到).输入文件的第 2 到 n 行,每行描述一个除SZ之外的城市.其中第 v 行包含 5 个非负整数 $f_v,s_v,p_v,q_v,l_v$,分别表示城市 v 的父亲城市,它到

【bzoj1316】树上的询问 树的点分治+STL-set

题目描述 一棵n个点的带权有根树,有p个询问,每次询问树中是否存在一条长度为Len的路径,如果是,输出Yes否输出No. 输入 第一行两个整数n, p分别表示点的个数和询问的个数. 接下来n-1行每行三个数x, y, c,表示有一条树边x→y,长度为c. 接下来p行每行一个数Len,表示询问树中是否存在一条长度为Len的路径. 输出 输出有p行,Yes或No. 样例输入 6 4 1 2 5 1 3 7 1 4 1 3 5 2 3 6 3 1 8 13 14 样例输出 Yes Yes No Yes

【bzoj4016】[FJOI2014]最短路径树问题 堆优化Dijkstra+DFS树+树的点分治

题目描述 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择总长度最短的路径走.若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小.注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小).到达该点后按原路返回,然后往其他点走,直到所有点都走过. 可以知道,经过的边会构成一棵最短路径树.请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长

HDU4812 D Tree(树的点分治)

题目大概说给一棵有点权的树,输出字典序最小的点对,使这两点间路径上点权的乘积模1000003的结果为k. 树的点分治搞了.因为是点权过根的两条路径的LCA会被重复统计,而注意到1000003是质数,所以这个用乘法逆元搞一下就OK了.还有要注意“治”的各个实现,把时间复杂度“控制”在O(nlogn). WA了几次,WA在漏了点到子树根的路径,还有每次分治忘了清空数组. 1 #include<cstdio> 2 #include<cstring> 3 #include<algor

【bzoj3362/3363/3364/3365】[Usaco2004 Feb]树上问题杂烩 并查集/树形dp/LCA/树的点分治

题目描述 农夫约翰有N(2≤N≤40000)个农场,标号1到N,M(2≤M≤40000)条的不同的垂直或水平的道路连结着农场,道路的长度不超过1000.这些农场的分布就像下面的地图一样, 图中农场用F1..F7表示, 每个农场最多能在东西南北四个方向连结4个不同的农场.此外,农场只处在道路的两端.道路不会交叉且每对农场间有且仅有一条路径.邻居鲍伯要约翰来导航,但约翰丢了农场的地图,他只得从电脑的备份中修复了.每一条道路的信息如下: 从农场23往南经距离10到达农场17 从农场1往东经距离7到达农

poj 1741(树的点分治)

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) not exceed k

poj 1741 树的点分治(入门)

Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 18205   Accepted: 5951 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