大二寒假第四周学习笔记

过年摸鱼了一个星期

周六

P3806 【模板】点分治1

用来处理树上的路径问题

其实挺暴力的,但是按照重心分解来处理却是nlogn的

#include <bits/stdc++.h>
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
#define rep(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int N = 1e4 + 10;
const int M = 1e7 + 10;
vector<pair<int, int>> g[N];
int q[N], ans[N], maxp[N], vis[N], siz[N], rt, sum, n, m;
vector<int> ve;
bool judge[M];

void getrt(int u, int fa)
{
    siz[u] = 1; maxp[u] = 0;               //注意这里初始化maxp
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(v == fa || vis[v]) continue;
        getrt(v, u);
        siz[u] += siz[v];
        maxp[u] = max(maxp[u], siz[v]);
    }
    maxp[u] = max(maxp[u], sum - siz[u]);
    if(maxp[u] < maxp[rt]) rt = u;
}

void getdis(int u, int fa, int d)
{
    if(d <= 1e7) ve.push_back(d);       //询问到1e7 总和会到1e8
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v] || v == fa) continue;
        getdis(v, u, d + w);
    }
}

void solve(int u)
{
    vector<int> tmp;               //防止memset
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v]) continue;
        ve.clear();
        getdis(v, u, w);

        for(int x: ve)
            _for(i, 1, m)
                if(q[i] >= x)
                    ans[i] |= judge[q[i] - x];
        for(int x: ve)
        {
            judge[x] = true;
            tmp.push_back(x);
        }
    }
    for(int x: tmp) judge[x] = false;
}

void divide(int u)
{
    vis[u] = 1;        //删除u
    solve(u);          //处理经过u的路径
    for(auto x: g[u])
    {
        int v = x.first;
        if(vis[v]) continue;
        maxp[rt = 0] = sum = siz[v];   //这里初始化sum是siz[v]
        getrt(v, 0);
        getrt(rt, 0);
        divide(rt);
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    _for(i, 1, n - 1)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }
    _for(i, 1, m) scanf("%d", &q[i]);

    judge[0] = true;       //注意要初始化0
    maxp[rt = 0] = sum = n; //初始化rt
    getrt(1, 0);            //找重心
    getrt(rt, 0);           //更新正确的siz数组
    divide(rt);

    _for(i, 1, m) puts(ans[i] ? "AYE" : "NAY");

    return 0;
}

P2634 [国家集训队]聪聪可可(点分治)

和上一道题很像,有一个坑点

处理出来的是路径的条数,而这道题(1, 2)和(2, 1)是不一样的

所以得出答案之后要处理一下

#include <bits/stdc++.h>
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
#define rep(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

typedef long long ll;
const int N = 2e4 + 10;
vector<pair<int, int>> g[N];
int maxp[N], siz[N], vis[N], rt, sum, n;
vector<int> ve;
ll ans, cnt[3];

void getrt(int u, int fa)
{
    maxp[u] = 0; siz[u] = 1;
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v] || v == fa) continue;
        getrt(v, u);
        siz[u] += siz[v];
        maxp[u] = max(maxp[u], siz[v]);
    }
    maxp[u] = max(maxp[u], sum - siz[u]);
    if(maxp[rt] > maxp[u]) rt = u;
}

void getdis(int u, int fa, int d)
{
    ve.push_back(d);
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v] || v == fa) continue;
        getdis(v, u, d + w);
    }
}

void solve(int u)
{
    ans++;            //自己到自己
    cnt[1] = cnt[2] = 0; cnt[0] = 1;
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v]) continue;
        ve.clear();
        getdis(v, u, w);

        for(int x: ve)
            rep(i, 0, 3)
                if((x + i) % 3 == 0)
                    ans += cnt[i];
        for(int x: ve) cnt[x % 3]++;
    }
}

void divide(int u)
{
    vis[u] = 1;
    solve(u);
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v]) continue;
        maxp[rt = 0] = sum = siz[v];
        getrt(v, 0);
        getrt(rt, 0);
        divide(rt);
    }
}

ll gcd(ll a, ll b) { return !b ? a : gcd(b, a % b); }

int main()
{
    scanf("%d", &n);
    _for(i, 1, n - 1)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        w %= 3;
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }

    maxp[rt = 0] = sum = n;
    getrt(1, 0);
    getrt(rt, 0);
    divide(rt);

    ll a = (ans - n) * 2 + n, b = 1LL * n * n;
    ll d = gcd(a, b);
    printf("%lld/%lld\n", a / d, b / d);

    return 0;
}

P4149 [IOI2011]Race(点分治)

用unordered_map存一下之前子树中。权值和为x的最少边数即可

#include <bits/stdc++.h>
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
#define rep(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

typedef long long ll;
const int N = 2e5 + 10;
vector<pair<int, int>> g[N];
int maxp[N], siz[N], vis[N], rt, sum, n, k, ans = 1e9;
vector<pair<int, int>> ve;

void getrt(int u, int fa)
{
    maxp[u] = 0; siz[u] = 1;
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v] || v == fa) continue;
        getrt(v, u);
        siz[u] += siz[v];
        maxp[u] = max(maxp[u], siz[v]);
    }
    maxp[u] = max(maxp[u], sum - siz[u]);
    if(maxp[rt] > maxp[u]) rt = u;
}

void getdis(int u, int fa, int d, int dep)
{
    if(d > k) return;
    ve.push_back({d, dep});
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v] || v == fa) continue;
        getdis(v, u, d + w, dep + 1);
    }
}

void solve(int u)
{
    unordered_map<int, int> mp;
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v]) continue;
        ve.clear();
        getdis(v, u, w, 1);

        for(auto x: ve)
        {
            if(k > x.first && mp[k - x.first]) ans = min(ans, mp[k - x.first] + x.second);
            if(k == x.first) ans = min(ans, x.second);
        }
        for(auto x: ve)
        {
            if(!mp[x.first]) mp[x.first] = x.second;
            else mp[x.first] = min(mp[x.first], x.second);
        }
    }
}

void divide(int u)
{
    vis[u] = 1;
    solve(u);
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v]) continue;
        maxp[rt = 0] = sum = siz[v];
        getrt(v, 0);
        getrt(rt, 0);
        divide(rt);
    }
}

int main()
{
    scanf("%d%d", &n, &k);
    _for(i, 1, n - 1)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        u++; v++;
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }

    maxp[rt = 0] = sum = n;
    getrt(1, 0);
    getrt(rt, 0);
    divide(rt);

    printf("%d\n", ans == 1e9 ? -1 : ans);

    return 0;
}

P4178 Tree(点分治+树状数组)

和前面差不多

需要用一个数据结构来维护前缀和,单点修改 

树状数组即可

注意细节,树状数组不能处理0,因此在求和的时候初始化为1

#include <bits/stdc++.h>
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
#define rep(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

typedef long long ll;
const int N = 4e4 + 10;
vector<pair<int, int>> g[N];
int maxp[N], vis[N], siz[N], cnt[N], f[N], rt, sum, n, m, k;
vector<int> ve;
ll ans;

void getrt(int u, int fa)
{
    siz[u] = 1; maxp[u] = 0;
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(v == fa || vis[v]) continue;
        getrt(v, u);
        siz[u] += siz[v];
        maxp[u] = max(maxp[u], siz[v]);
    }
    maxp[u] = max(maxp[u], sum - siz[u]);
    if(maxp[u] < maxp[rt]) rt = u;
}

void getdis(int u, int fa, int d)
{
    if(d > k) return;
    ve.push_back(d);
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v] || v == fa) continue;
        getdis(v, u, d + w);
    }
}

int lowbit(int x) { return x & -x; }

void add(int x)
{
    for(; x <= k; x += lowbit(x))
        f[x]++;
}

int cal(int x)
{
    int res = 1;
    for(; x; x -= lowbit(x))
        res += f[x];
    return res;
}

void solve(int u)
{
    memset(f, 0, sizeof f);
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v]) continue;
        ve.clear();
        getdis(v, u, w);
        for(int x: ve) ans += cal(k - x);
        for(int x: ve) add(x);
    }
}

void divide(int u)
{
    vis[u] = 1;
    solve(u);
    for(auto x: g[u])
    {
        int v = x.first;
        if(vis[v]) continue;
        maxp[rt = 0] = sum = siz[v];
        getrt(v, 0);
        getrt(rt, 0);
        divide(rt);
    }
}

int main()
{
    scanf("%d", &n);
    _for(i, 1, n - 1)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }
    scanf("%d", &k);

    maxp[rt = 0] = sum = n;
    getrt(1, 0);
    getrt(rt, 0);
    divide(rt);

    printf("%lld\n", ans);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值