poj 1741 洛谷 3806 【模板】点分治1 树的分治 点分治


题目背景

感谢hzwer的点分治互测。

题目描述

给定一棵有n个点的树

询问树上距离为k的点对是否存在。

输入输出格式

输入格式:

n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径

接下来m行每行询问一个K

输出格式:

对于每个K每行输出一个答案,存在输出“AYE”,否则输出”NAY”(不包含引号)

输入输出样例

输入样例#1:
2 1
1 2 2
2
输出样例#1:
AYE










https://www.luogu.org/problem/show?pid=3806#sub



#include <bits/stdc++.h>
using namespace std;
#define mo 100005
#define mk make_pair
vector< pair<int ,int > > d[mo];//箭头加空格
int sz[mo];//树的重心..
int sd[mo];//树的重心..
int dist[mo];//计算到重心的距离
int shux[mo];//标记点是从哪个子树下的
bool de[10000005];//标记k是否出现过 
int jl[mo];//删除树的重心标记
int zz;
void dfsz(int node,int dad,int &maxs,int &v,int n) //计算树的重心
{
    sz[node]=1;
    for(int i=0;i<d[node].size();i++)
    {
        int son=d[node][i].first;
        if(son!=dad&&!jl[son])
        {
            dfsz(son,node,maxs,v,n);
            sz[node]+=sz[son];
            if(sz[son]<maxs)
            {
                maxs=sz[son];
                v=son;
            }
        }
    }
    if(n-sz[node]<maxs)
    {
        maxs=n-sz[node];
        v=node;
    }
}
void dis(int node,int dad,int s,int &l,int j) //计算点到重心的距离
{
    dist[l]=s;
    shux[l]=j;//标记属于哪个子树
    l++;
    if(dad==0) j=2;
    for(int i=0;i<d[node].size();i++)
    {
        int son=d[node][i].first;
        int ls=d[node][i].second;
        if(dad==0) j++;//只有dad为初始值的时候分子树编号
        if(son!=dad&&!jl[son])
        {
            dis(son,node,s+ls,l,j);
        }
    }
}
int shu(int k,int v,int d)//标记k是否出现过
{
    int l=0;
    memset(shux,0,sizeof(shux));
    dis(v,0,d,l,0);

    for(int i=0;i<l;i++)
    {
        for(int j=i+1;j<l;j++)
        {
            if(shux[i]!=shux[j])//只有不同的子树可以相加
            {
                //cout<<shux[i]<<' '<<shux[j]<<' '<<dist[i]<<' '<<dist[j]<<' '<<dist[i]+dist[j]<<' '<<i<<' '<<j<<endl;
                de[dist[i]+dist[j]]=1;
            }
        }
    }
    return 0;
}
void dx(int node,int dad,int &l)//计算树的大小
{
    l++;
    for(int i=0;i<d[node].size();i++)
    {
        int son=d[node][i].first;
        if(son!=dad&&!jl[son])
            dx(son,node,l);
    }
}

void dfs(int x,int &ans,int k)//树的分治递归
{
    int n=0;
    dx(x,0,n);//首先判断大小
    int mins=10000005,v=x;
    dfsz(x,0,mins,v,n);//之后机选树的重心
    shu(k,v,0); //再去寻找距离重心的距离和距离为k 的数对
    jl[v]=1;
    for(int i=0;i<d[v].size();i++)
    {
        int son=d[v][i].first;
        if(!jl[son])
        {
            int le=d[v][i].second;
            //ans-=shu(k,son,le);
            dfs(son,ans,k);
        }
    }
   //jl[v]=0;
}

int main()
{
    int n,m,k;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(de,false,sizeof(de));
        int x,y,z;
        for(int i=1;i<n;i++)
        {
            //cin>>x>>y>>z;
            scanf("%d%d%d",&x,&y,&z);
            d[x].push_back(mk(y,z));
            d[y].push_back(mk(x,z));
        }
        int ans=0;
        dfs(1,ans,k);
        for(int i=0;i<m;i++)
        {
            //cin>>k;
            scanf("%d",&k);
            if(de[k]) //cout<<"AYE"<<endl;
            printf("AYE\n");
            else //cout<<"NAY"<<endl;
            printf("NAY\n");
        }
    }
}



Tree
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 24713 Accepted: 8256

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 exceed k. 
Write a program that will count how many pairs which are valid for a given tree. 

Input

The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l. 
The last test case is followed by two zeros. 

Output

For each test case output the answer on a single line.

Sample Input

5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0

Sample Output

8

Source


http://poj.org/problem?id=1741


#include <bits/stdc++.h>
using namespace std;
#define mo 100005
#define mk make_pair
vector< pair<int ,int > > d[mo];//箭头加空格
int sz[mo];//树的重心..
int sd[mo];//树的重心..
int dist[mo];//距离重心的距离
int jl[mo];//删重心的标记

void dfsz(int node,int dad,int &maxs,int &v,int n) //寻找重心,n是计算树的大小得出
{
    sz[node]=1;
    for(int i=0;i<d[node].size();i++)
    {
        int son=d[node][i].first;

        if(son!=dad&&!jl[son])
        {
            dfsz(son,node,maxs,v,n);
            sz[node]+=sz[son];
            if(sz[son]<maxs)
            {
                maxs=sz[son];
                v=son;
            }
        }
    }
    if(n-sz[node]<maxs)
    {
        maxs=n-sz[node];
        v=node;
    }
}
void dis(int node,int dad,int s,int &l)//求其它点到重心的距离
{
    dist[l++]=s;
    for(int i=0;i<d[node].size();i++)
    {
        int son=d[node][i].first;
        int ls=d[node][i].second;
        if(son!=dad&&!jl[son])
            dis(son,node,s+ls,l);
    }
}
int shu(int k,int v,int d)//数数有多少歌k对
{
    int l=0;
    dis(v,0,d,l);
    sort(dist,dist+l);
    int i=0,j=l-1;
    int ans=0;
    while(i<j)
    {

        while(dist[i]+dist[j]>k) j--;
        ans+=j-i;
        i++;
    }
    return ans;
}
void dx(int node,int dad,int &l)//计算树的大小,为了算树的重心
{
    l++;
    for(int i=0;i<d[node].size();i++)
    {
        int son=d[node][i].first;
        if(son!=dad&&!jl[son])
            dx(son,node,l);
    }
}
void dfs(int x,int &ans,int k)//递归分治
{
    int n=0;
    dx(x,0,n);//首先确定树的大小。确定n的大小
    int mins=10000005,v=x;
    dfsz(x,0,mins,v,n);//计算树的重心 ,将n带入
    ans+=shu(k,v,0); //数多少个k
    jl[v]=1;
    for(int i=0;i<d[v].size();i++)
    {
        int son=d[v][i].first;
        if(!jl[son])
        {
            int le=d[v][i].second;
            ans-=shu(k,son,le);//子树中重复的减去
            dfs(son,ans,k);
        }
    }
}

int main()
{
    int n,k;
    while(cin>>n>>k&&n+k)
    {
        int x,y,z;
        for(int i=1;i<n;i++)
        {
            cin>>x>>y>>z;
            d[x].push_back(mk(y,z));
            d[y].push_back(mk(x,z));
        }
        int ans=0;
        dfs(1,ans,k);
        cout<<ans<<endl;
    }
}
















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值