[模板] 点分治

之前搞了一个树的中心,结果当时把点分治给扔下了,现在搞一搞。其实点分治的分治思想很明显,就是把树切成一个个小树,然后在重心的位置再分治就行了。

代码实现有一定困难,但就我觉得前一个函数和树刨的dfs1很像吗,详情见代码。

题干:

题目背景

感谢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
说明
对于30%的数据n<=100
对于60%的数据n<=1000,m<=50
对于100%的数据n<=10000,m<=100,c<=1000,K<=10000000

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define duke(i,a,n) for(int i = a;i <= n;i++)
#define lv(i,a,n) for(int i = a;i >= n;i--)
#define clean(a) memset(a,0,sizeof(a))
const int INF = 1 << 30;
typedef long long ll;
typedef double db;
template <class T>
void read(T &x)
{
    char c;
    bool op = 0;
    while(c = getchar(), c < ‘0‘ || c > ‘9‘)
        if(c == ‘-‘) op = 1;
    x = c - ‘0‘;
    while(c = getchar(), c >= ‘0‘ && c <= ‘9‘)
        x = x * 10 + c - ‘0‘;
    if(op) x = -x;
}
template <class T>
void write(T x)
{
    if(x < 0) putchar(‘-‘), x = -x;
    if(x >= 10) write(x / 10);
    putchar(‘0‘ + x % 10);
}
struct node
{
    int l,r,nxt,w;
} a[40005];
int lst[40005],len = 0,n,m;
void add(int x,int y,int w)
{
    a[++len].l = x;
    a[len].r = y;
    a[len].w = w;
    a[len].nxt = lst[x];
    lst[x] = len;
}
int qu[405],maxp[40005],sum,rt = 0,siz[40005];
int vis[40005],judge[10000000],test[10000000];
int q[40005],dis[40005],rem[40005];
void getrt(int u,int pa)
{
    siz[u] = 1;
    maxp[u] = 0;
    for(int k = lst[u]; k; k = a[k].nxt)
    {
        int y = a[k].r;
        if(y == pa || vis[y])
            continue;
        getrt(y,u);
        siz[u] += siz[y];
        maxp[u] = max(maxp[u],siz[y]);
    }
    maxp[u] = max(maxp[u],sum - siz[u]);
    if(maxp[u] < maxp[rt])
        rt = u;
}
void getdis(int u,int fa)
{
    rem[++rem[0]] = dis[u];//主要是这一句,就是rem中存了所有可能的长度
    for(int k = lst[u]; k; k = a[k].nxt)
    {
        int y = a[k].r;
        if(y == fa || vis[y] == 1)
            continue;
        dis[y] = dis[u] + a[k].w;//记录子树中的距离
        getdis(y,u);
    }
}
void calc(int u)
{
    int p = 0;
    for(int k = lst[u]; k; k = a[k].nxt)
    {
        int y = a[k].r;
        if(vis[y] == 1)
            continue;
        rem[0] = 0;
        dis[y] = a[k].w;
        getdis(y,u);
        lv(j,rem[0],1)
        {
            duke(i,1,m)
            {
                if(qu[i] >= rem[j])
                    test[i] |= judge[qu[i] - rem[j]];//尝试匹配
            }
        }
        lv(j,rem[0],1)
        {
            q[++p] = rem[j];//就是一个过程量
            judge[rem[j]] = 1;
        }
    }
    duke(i,1,p)
    judge[q[i]] = 0;//清空当前子树judge数组
}
void solve(int u)
{
    vis[u] = judge[0] = 1;
    calc(u);
    for(int k = lst[u]; k; k = a[k].nxt)
    {
        int y = a[k].r;
        if(vis[y])
            continue;
        sum = siz[y];
        maxp[rt = 0] = INF;
        getrt(y,0);
        solve(rt);
    }
}
int main()
{
    read(n);
    read(m);
    duke(i,1,n - 1)
    {
        int x,y,w;
        read(x);
        read(y);
        read(w);
        add(x,y,w);
        add(y,x,w);
    }
    duke(i,1,m)
    read(qu[i]);
    maxp[rt] = sum = n;
    getrt(1,0);
//    cout<<"QAQ"<<endl;
    solve(rt);
    duke(i,1,m)
    {
        if(test[i])
            printf("AYE\n");
        else
            printf("NAY\n");
    }
    return 0;
}

原文地址:https://www.cnblogs.com/DukeLv/p/9693656.html

时间: 2024-10-08 14:48:42

[模板] 点分治的相关文章

模板&#183;点分治(luogu P3806)

[模板]洛谷·点分治 1.求树的重心 树的重心:若A点的子树中最大的子树的size[] 最小时,A为该树的中心 步骤: 所需变量:siz[x] 表示 x 的子树大小(含自己),msz[x] 表示 其子树中最大的子树的大小,sum表示当前子树所有节点个数,root表示当前子树根节点 处理出siz[x],msz[x] 按最大子树最小的标准处理出root inline void GetRoot(int x,int fa){ siz[x]=1;msz[x]=0;siz[x]//表示 x 的子树大小(含自

Luogu 4721 【模板】分治 FFT

还不会这题的多项式求逆的算法. 发现每一项都是一个卷积的形式,那么我们可以使用$NTT$来加速,直接做是$O(n^2logn)$的,我们考虑如何加速转移. 可以采用$cdq$分治的思想,对于区间$[l, r]$中的数,先计算出$[l, mid]$中的数对$[mid + 1, r]$中的数的贡献,然后直接累加到右边去. 容易发现,这样子每一次需要用向量$[l,l + 1, l +  2, \dots, mid]$卷上$g$中$[1, 2, \dots, r - l]$. 时间复杂度$O(nlog^

【模板】分治 FFT

题目大意:给定长度为 \(n - 1\) 的序列 \(g\),求 \(f\) 序列,其中 \(f\) 为 \[ f[i]=\sum_{j=1}^{i} f[i-j] g[j] \] 学会了分治 \(fft\). 发现这个式子中也含有卷积,但是这是一个递推式,即:\(f\) 数组是未知的. 考虑分治策略,即:假设已经算出区间 \([l, mid]\) 的 \(f\) 值,现在要计算区间 \([mid + 1, r]\) 的 \(f\). 考虑左半部分对右半部分的贡献,对于 \[x \in [mid

[题解] Luogu P4721 【模板】分治 FFT

分治FFT的板子为什么要求逆呢 传送门 这个想法有点\(cdq\)啊,就是考虑分治,在算一段区间的时候,我们把他分成两个一样的区间,然后先做左区间的,算完过后把左区间和\(g\)卷积一下,这样就可以算出左区间里的\(f\)对右边的贡献,然后再算右边的就好了. 手玩一组样例吧:g=[0,3,1,2](默认\(g[0] = 0\)) 一开始,只有f[0]=1 f: [1 0|0 0] 然后我们从中间分开来,先算左边的 f: [1|0|0 0] 然后在分下去我们会找到\(f[0]\),就拿这一段和\(

【模板】分治FFT

题意 分析 如果我们已经求得了 f[L],f[L+1] ... f[mid],他们均能对f[mid+1],f[mid+2]...,f[R]产生贡献 对于x ∈ [mid+1,r] f[x] += \sum_{i=L}^{mid}(f[i]*g[x-i]) 等式右边满足卷积模式 具体看代码 代码 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int n; 5 ll gg[400005],f[4

P4721 【模板】分治 FFT

\(\color{#0066ff}{ 题目描述 }\) 给定长度为 \(n-1\) 的数组 \(g[1],g[2],..,g[n-1]\),求 \(f[0],f[1],..,f[n-1]\),其中 \(f[i]=\sum_{j=1}^if[i-j]g[j]\) 边界为 \(f[0]=1\) .答案模 \(998244353\) . \(\color{#0066ff}{输入格式}\) 第一行一个正整数 \(n\) . 第二行共 \(n-1\) 个非负整数 \(g[1],g[2],..,g[n-1]

牛客练习赛11 B trie树+拓扑判环 E 分治求平面最近点对

牛客练习赛11 B  假的字符串题意:给定n个字符串,互不相等,你可以任意指定字符之间的大小关系(即重定义字典序),求有多少个串可能成为字典序最小的串,并输出它们. tags:好题 对于一个字符串, 1]如有其它字符串是它的前缀,那肯定不可能.这个直接用字典树处理就可以. 2]但如果以这个字符串为最小,怎么判定其它字符串不会矛盾呢? 其实矛盾的情况详细一点说是: 比如要以  abcd 为最小, 但又有另一个字符串 aba ,这就矛盾了. 对这种情况,在跑字典树的时候,我们对有相同父亲结点的多个儿

从头开始的点分治

今天发现之前有篇随笔忘发布了-- TREE [题意]给你一棵树,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K. [分析]考虑树根rt,可以处理出rt到其子树中的节点的距离,设为dis[].利用dis数组的值,我们可以轻易地得到所有"端点在rt的子树中,长度≤k的,且经过rt的路径"数量(具体做法是将dis的值排序,然后双指针扫描).要去除其中的非简单路径数量,只需要对rt的每个"儿子的子树"做一遍类似的过程(区别在于儿子的子树中的路径长度需要≤k-l

【Codeforces553E_CF553E】Kyoya and Train(概率_CDQ分治_FFT)

题目 Codeforces 553E 我为什么要写这道题?因为说到 553 ,你有没有想到 -- 翻译 这个 Kyoya Ootori 怎么看都像是日语名字但是我是真查不出来对应的汉字是什么(好像是什么京屋鳳之类的),方便起见直接认为主人公叫张三. 题目名称:张三和火车 描述 张三想坐火车去学校.有 \(n\) 个火车站和在不同车站间开行的 \(m\) 条单向火车线路.张三现在在 \(1\) 号火车站,学校在 \(n\) 号火车站.他必须买票才能坐火车,并且坐火车也需要花费时间.然而,由于火车存