P3806 【模板】点分治1

学自:bztMinamoto
点分治是个很容易学的算法,就像拉格朗日插值法,生成函数一样,遇到这种题目不会就是不会,但是算法又很容易学,学起来性价比高,比较推荐大家也学学这些算法…
P3806 【模板】点分治1
点分治入门题
n^2写法

#include<bits/stdc++.h>
#define pi pair<int, int>
#define mk make_pair
using namespace std;
const int maxn = 1e4 + 5, N = 1e7 + 5, inf = 1e9;
int cnt[N], p[maxn], sz[maxn], d[maxn], vis[maxn], size, rt, mn, M;
vector<pi> G[maxn];
void getroot(int u, int fa) {
    int cat = 0;
    sz[u] = 1;
    for (auto tmp : G[u])
    if (tmp.first != fa && !vis[tmp.first]) {
        int v = tmp.first;
        getroot(v, u);
        sz[u] += sz[v];
        cat = max(cat, sz[v]);
    }
    cat = max(cat, size - sz[u]);
    if (cat < mn)
        mn = cat, rt = u;
}
void getdis(int u, int val, int fa) {
    d[++M] = val;
    for (auto tmp : G[u])
        if (tmp.first != fa && !vis[tmp.first])
            getdis(tmp.first, val + tmp.second, u);
}
void solve(int u, int val, int opt) {
    M = 0;
    getdis(u, val, 0);
    for (int i = 1; i <= M; i++)
        for (int j = 1; j < i; j++)
            if (d[i] + d[j] < N)
                cnt[d[i] + d[j]] += opt;
}
void dfs(int u, int fa) {
    solve(u, 0, 1);
    vis[u] = 1;
    int tot = size;
    for (auto tmp : G[u])
    if (tmp.first != fa && !vis[tmp.first]) {
        int v = tmp.first;
        solve(v, tmp.second, -1);
        mn = inf, rt = 0;
        size = (sz[v] > sz[u]) ? tot - sz[u] : sz[v];
        getroot(v, 0);
        dfs(rt, 0);
    }
}
int main() {
    int n, q, u, v, w, x;
    scanf("%d%d", &n, &q);
    for (int i = 1; i < n; i++) {
        scanf("%d%d%d", &u, &v, &w);
        G[u].push_back(mk(v, w));
        G[v].push_back(mk(u, w));
    }
    size = n;
    mn = inf;
    getroot(1, 0);
    dfs(rt, 0);
    while (q--) {
        scanf("%d", &x);
        if (cnt[x])
            puts("AYE");
        else
            puts("NAY");
    }
}

qnlogn写法:

#include<bits/stdc++.h>
#define pi pair<int, int>
#define mk make_pair
using namespace std;
const int maxn = 1e4 + 5, N = 1e7 + 5, inf = 1e9;
int cnt[N], p[maxn], sz[maxn], d[maxn], vis[maxn], size, rt, mn, M, qry[maxn], q, vis2[N];
vector<pi> G[maxn];
void getroot(int u, int fa) {
    int cat = 0;
    sz[u] = 1;
    for (auto tmp : G[u])
    if (tmp.first != fa && !vis[tmp.first]) {
        int v = tmp.first;
        getroot(v, u);
        sz[u] += sz[v];
        cat = max(cat, sz[v]);
    }
    cat = max(cat, size - sz[u]);
    if (cat < mn)
        mn = cat, rt = u;
}
void getdis(int u, int val, int fa) {
    d[++M] = val;
    for (auto tmp : G[u])
        if (tmp.first != fa && !vis[tmp.first])
            getdis(tmp.first, val + tmp.second, u);
}
void solve(int u, int val, int opt) {
    M = 0;
    getdis(u, val, 0);
    for (int i = 1; i <= M; i++) {
        for (int j = 1; j <= q; j++)
        if (d[i] <= qry[j]) {
            cnt[j] += opt * vis2[qry[j] - d[i]];
            if (d[i] == qry[j])
                cnt[j] += opt;
        }
        if (d[i] < N)
            vis2[d[i]]++;
    }
    for (int i = 1; i <= M; i++)
        if (d[i] < N)
            vis2[d[i]] = 0;
}
void dfs(int u, int fa) {
    solve(u, 0, 1);
    vis[u] = 1;
    int tot = size;
    for (auto tmp : G[u])
    if (tmp.first != fa && !vis[tmp.first]) {
        int v = tmp.first;
        solve(v, tmp.second, -1);
        mn = inf, rt = 0;
        size = (sz[v] > sz[u]) ? tot - sz[u] : sz[v];
        getroot(v, 0);
        dfs(rt, 0);
    }
}
int main() {
    int n, u, v, w, x;
    scanf("%d%d", &n, &q);
    for (int i = 1; i < n; i++) {
        scanf("%d%d%d", &u, &v, &w);
        G[u].push_back(mk(v, w));
        G[v].push_back(mk(u, w));
    }
    for (int i = 1; i <= q; i++)
        scanf("%d", &qry[i]);
    size = n;
    mn = inf;
    getroot(1, 0);
    dfs(rt, 0);
    for (int i = 1; i <= q; i++) {
        if (cnt[i])
            puts("AYE");
        else
            puts("NAY");
    }
}

有网友对这题n^2写法复杂度不太理解,我简单证明一下:
复杂度证明:每个子树计算次数: s i z e ∗ s i z e / 2 size * size / 2 sizesize/2

所有重心为根的子树最坏总计算次数: 1 2 ( n 2 + n 2 4 ∗ 2 + n 2 16 ∗ 4 + n 2 64 ∗ 8 + . . . . ) \frac{1}{2}(n^2+\frac{n^2}{4}*2+\frac{n^2}{16}*4+\frac{n^2}{64}*8+....) 21(n2+4n22+16n24+64n28+....)

我们知道重心树一共有log层,公式化简: 1 2 n 2 ( 1 − 1 2 l o g ) 1 − 1 2 = n 2 \frac{1}{2}\frac{{}n^{2}(1-\frac{1}{2^{log}})}{1-\frac{1}{2}}=n^{2} 21121n2(12log1)=n2

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值