洛谷P3806点分治题解

题目链接:https://www.luogu.org/problem/P3806

题目描述

给定一棵有n个点的树
询问树上距离为k的点对是否存在。

输入格式

n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径
接下来m行每行询问一个K
数据范围:对于100%的数据n<=10000,m<=100,c<=10000,K<=10000000

输出格式

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

题解

  虽说是点分治的模板题,但也加大了难度,多组询问,意味着离线处理,每次分治的时候,对每个答案分别判断一遍,每一层复杂度为O(n*m),达到了1e6,不过还好log(n)层,所以最终也就1e7的复杂度。

变量声明:

  1. temp数组:记录当前子树所有点到重心的距离
  2. judge数组:相当于将temp数组离散化,记录某一点到重心的距离存在多少条
  3. test数据:将k组询问离线处理
      如何判断某一长度在树中是否出现?在当前分治层,假设求长度为a的是否存在,对temp进行扫描,已知 temp[i] 存在,只需要查询 a-temp[i]是否存在即可,judge直接判断。因为存在某一点对在同一子树的情况,所以还要用去除这些长度。
      设有到重心的距离有a、b,要求长度为 c 的点对是否存在,且a+b=c,分两种情况:
    1、当a=b时,存在的点对为 i = 1 j u d g e [ a ] \sum_{i=1}^{judge[a]} i
    2、当a!=b时,存在的点对为 judge[a]*judge[b] (乘法原理)。
      需要注意 judge[i] 用过一次后不能再用,需要将 judge[a] 和 judge[b] 置零,避免重复计算。最后,如果是第一次进入当前层,test[i]+=如上答案,如果是排除答案的时候进入,则test[i]-=如上答案。最终,test将保存了,每一种路径的可能性有多少可能选择的点对种数。

代码

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false)
const int N = 20020, INF = 0x7f7f7f7f;

int n,m,head[N*2],num,tot,ans;//tot记录当前子树点数
int dis[N],flag[N],temp[N];
//dis记录子树每一点到根节点的距离,flag用于删除根节点,temp总汇到根节点的距离
int query[105];
int test[105];//当前路径有几条

int size[N],Max[N],root;
struct edge
{
    int next,to,len;
} G[N*2];
void add(int from,int to,int len)
{
    G[++num].next=head[from];
    G[num].to=to;
    G[num].len=len;
    head[from]=num;
}

inline void input(void)
{
    for (int i=1; i<n; i++)
    {
        int x,y,v;
        cin>>x>>y>>v;
        add(x,y,v), add(y,x,v);
    }
}

inline void dp(int fa,int cur)//求树的重心
{
    size[cur] = 1, Max[cur] = 0;
    for (int i=head[cur]; i; i=G[i].next)
    {
        int v = G[i].to;
        if ( flag[v] || v == fa ) continue;
        dp(cur,v);
        size[cur] += size[v];
        Max[cur] = max( Max[cur], size[v] );
    }
    Max[cur] = max( Max[cur], tot - size[cur] );
    if ( Max[root] > Max[cur] ) root = cur;
}

inline void dfs(int fa,int cur)
{
    temp[ ++temp[0] ] = dis[cur];
    for (int i=head[cur]; i; i=G[i].next)
    {
        int v = G[i].to;
        if ( v == fa || flag[v] ) continue;
        dis[v] = dis[cur] + G[i].len;
        dfs(cur,v);
    }
}

const int inf=10000007;
int judge[inf];
void calc(int x,int len,bool f2)
{
    dis[x] = len;
    temp[0] = 0;//temp[0]记录temp数组的长度
    dfs(0,x);

    for(int i=0; i<m; i++)
    {
        for(int j=1; j<=temp[0]; j++)
        if(temp[j]<inf)
            judge[temp[j]]++;

        int len=query[i];
        for(int j=1; j<=temp[0]; j++)
        {
            int len2=len-temp[j];
            if(len2<0)continue;

            if(judge[len2])
            {
                if(f2)
                {
                    if(len2!=temp[j])
                        test[i]+=judge[len2]*judge[temp[j]];
                    else
                        test[i]+=judge[len2]*(judge[len2]-1)/2;
                }
                else
                {
                    if(len2!=temp[j])
                        test[i]-=judge[len2]*judge[temp[j]];
                    else
                        test[i]-=judge[len2]*(judge[len2]-1)/2;
                }
                judge[len2]=0;
                judge[temp[j]]=0;
            }
        }

        for(int j=1;j<=temp[0];j++)
            if(temp[j]<inf)
                judge[temp[j]]=0;
    }
}

inline void divide(int x)
{
    flag[x] = true;//删去根节点
    calc(x,0,true);
    //cout<<"ans="<<ans<<"\n";
    for (int i=head[x]; i; i=G[i].next)
    {
        int y = G[i].to;
        if ( flag[y] ) continue;
        calc(y,G[i].len,false);//点对在同一子树的情况
        tot = size[y], root = 0;
        dp(0,y);
        divide(root);
    }
}

inline void reset(void)
{
    num = 0;
    memset( head, 0, sizeof head );
    memset( flag, 0, sizeof flag );
    ans = 0, tot = n;
    root = 0, Max[0] = INF;
}

int main(void)
{
    IOS;
    cin>>n>>m;
    reset();
    input();
    for(int i=0; i<m; i++)
        cin>>query[i];
    dp(0,1);
    divide(root);
    for(int i=0; i<m; i++)
        if(test[i])
            cout<<"AYE\n";
        else cout<<"NAY\n";
    return 0;
}
发布了41 篇原创文章 · 获赞 2 · 访问量 1234

猜你喜欢

转载自blog.csdn.net/qq_41418281/article/details/101977526