点分治题单

本文介绍了点分治算法在解决树形结构问题中的应用,包括求解点对距离、小于特定距离的点对数量等。通过二分查找和递归处理,实现了高效的解决方案,并提供了详细的代码实现,涉及了树的重心、子树距离信息统计等关键步骤。
摘要由CSDN通过智能技术生成

点分治中重点需要注意:

  1. 统计ans的时候,一定要注意子树中的ans和当前根节点的ans的处理关系
  2. 切记要注意重心的处理

一、P3806 【模板】点分治1

思路:

  1. 将一颗子树的点到根的距离存入到一个栈中,这个栈维护当前重心的所有子树的距离信息
  2. 我们在得子树点到根信息后,我们在枚举进行距离的判断,我们判断完再进行信息的入栈。
    code:
/*
 * @Author: 0iq_love_zy
 * @LastEditTime: 2021-02-21 18:35:44
 * @CSDN blog: https://blog.csdn.net/acm_durante
 * @E-mail: 1055323152@qq.com
 * @ProbTitle: 
 */
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define lowbit(x) ((x) & -(x))
#define lson p << 1 , l, mid 
#define rson p << 1 | 1, mid + 1, r
#define mem(a, b) memset(a, b, sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const double PI = acos(-1.0);
const int N = 2e4+5,M = 1e7+5;
int n,m,ask[105],cnt,head[N];
struct node{
    int v,w,net;
}e[N];
void add(int u,int v,int w){
    e[++cnt].v = v;
    e[cnt].w = w;
    e[cnt].net = head[u];
    head[u] = cnt;
}
int sz[N],sum,mx[N],root;
bool vis[N];

void get_root(int u,int fa){
    sz[u] = 1;
    mx[u] = 0;
    for(int i = head[u]; ~i ; i = e[i].net){
        int v = e[i].v;
        if(vis[v] || v == fa) continue;
        get_root(v,u);
        sz[u] += sz[v];
        mx[u] = max(mx[u],sz[v]);
    }
    mx[u] = max(mx[u],sum - mx[u]);
    if(mx[u] < mx[root]){
        root = u;
    }
}
int dis[N],dd[N],tot;
void get_dis(int u,int fa){
    dd[++tot] = dis[u];
    for(int i = head[u]; ~i ; i = e[i].net){
        int v = e[i].v, w = e[i].w;
        if(v == fa || vis[v]) continue;
        dis[v] = dis[u] + w;
        get_dis(v,u);
    }
}
queue<int>que;
bool tf[M],ans[M];
void dfz(int u,int fa){
    tf[0] = 1;
    que.push(0);
    vis[u] = 1;
    for(int i = head[u]; ~i ; i = e[i].net){//case1:经过u
        int v = e[i].v;
        if(v == fa || vis[v]) continue;
        dis[v] = e[i].w;
        tot = 0;
        get_dis(v,u);
        rep(j,1,tot)
            rep(z,1,m){
                if(ask[z] >= dd[j]) ans[z] |= tf[ask[z] - dd[j]];
            }
        rep(j,1,tot){
            if(dd[j] <= 1e7){
                tf[dd[j]] = 1;
                que.push(dd[j]);
            }
        }
    }
    while(!que.empty()){//清空
        tf[que.front()] = 0;
        que.pop();
    }
    for(int i = head[u]; ~i ; i = e[i].net){
        int v = e[i].v;
        if(vis[v] || v == fa) continue;
        sum = sz[v];
        root = 0;
        mx[root] = 2e9;
        get_root(v,u);
        get_root(root,-1);
        dfz(root,u);
    }
}
int main()
{
    IOS
    mem(head,-1);
    cin >> n >> m;
    rep(i,1,n-1){
        int u,v,w;
        cin >> u >> v >> w;
        add(u,v,w);
        add(v,u,w);
    }
    rep(i,1,m) cin >> ask[i];
    sum = n;
    root = 0;
    mx[0] = 2e9;
    get_root(1,-1);
    get_root(root,-1);
    dfz(root,-1);
    rep(i,1,m){
        if(ans[i]) cout << "AYE\n";
        else cout << "NAY\n";
    }
    return 0;
}

二、VK Cup 2012 Round 1 D. Distance in Tree

求距离为k的点对个数
思路:

  1. 本题我们用二分来处理答案的统计做到n*log(n)的时间复杂度来得到答案
  2. 注意处理点时两个点若在一个子树上的问题

code

/*
 * @Author: 0iq_love_zy
 * @LastEditTime: 2021-02-22 17:43:13
 * @CSDN blog: https://blog.csdn.net/acm_durante
 * @E-mail: 1055323152@qq.com
 * @ProbTitle: 
 */
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define lowbit(x) ((x) & -(x))
#define lson p << 1 , l, mid 
#define rson p << 1 | 1, mid + 1, r
#define mem(a, b) memset(a, b, sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const double PI = acos(-1.0);
const int N = 100500;
int n,k;
vector<int>e[N];
int mx[N],sz[N],root,S;
bool vis[N];
ll ans = 0;
void get_root(int u,int fa){
    mx[u] = 0;
    sz[u] = 1;
    for(auto v:e[u]){
        if(vis[v] || v == fa) continue;
        get_root(v,u);
        sz[u] += sz[v];
        mx[u] = max(mx[u],sz[v]);
    }
    mx[u] = max(mx[u],S - mx[u]);
    if(mx[u] < mx[root]) root = u;
}
int dis[N],dd[N],cnt;

void get_dis(int u,int fa,int di){
    for(auto v:e[u]){
        if(v == fa || vis[v]) continue;
        dd[++cnt] = di + 1;
        get_dis(v,u,dd[cnt]);
    }
}

ll get_ans(int u,int fa,int di){
    ll res = 0;
    cnt = 1;
    dd[cnt] = di; 
    get_dis(u,fa,di);
    sort(dd+1,dd+1+cnt);
    for(int i = 1 ; i <= cnt ; i++){
        int pos1 = lower_bound(dd + i + 1,dd + cnt + 1,k - dd[i]) - dd;//二分处理相同dis的个数
        int pos2 = upper_bound(dd + i + 1,dd + cnt + 1,k - dd[i]) - dd;
        res += pos2 - pos1;
    }
    return res;
}

void dfz(int u,int fa){
    vis[u] = 1, ans += get_ans(u,fa,0);
    for(auto v:e[u]){
        if(vis[v] || v == fa) continue;
        ans -= get_ans(v,u,1);
        S = sz[v];
        root = 0;
        mx[root] = INT_MAX;
        get_root(v,u);
        get_root(root,-1);
        dfz(root,-1);
    }
}
int main()
{
    IOS
    cin >> n >> k;
    rep(i,1,n-1){
        int u,v;
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    root = 0;
    S = n;
    mx[root] = INT_MAX;
    get_root(1,-1);
    get_root(root,-1);
    dfz(root,-1);
    cout << ans << "\n";
    return 0;
}

三、洛谷 P4178 Tree

求小于k的点对个数
思路:

  1. 和上题一样,但是需要注意我们来统计答案时,我们可以寻找k-dis的最大值的位置,在这个位置之前的都符合答案
    code:
/*
 * @Author: 0iq_love_zy
 * @LastEditTime: 2021-02-23 10:53:30
 * @CSDN blog: https://blog.csdn.net/acm_durante
 * @E-mail: 1055323152@qq.com
 * @ProbTitle: 
 */
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define lowbit(x) ((x) & -(x))
#define lson p << 1 , l, mid 
#define rson p << 1 | 1, mid + 1, r
#define mem(a, b) memset(a, b, sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const double PI = acos(-1.0);
const int N = 4e4+10;
struct node{
    int v,w;
};
vector<node>e[N];
int n,k;
int sz[N],mx[N],root,S;
bool vis[N];
void get_root(int u,int fa){
    mx[u] = 0;
    sz[u] = 1;
    for(auto i:e[u]){
        int v = i.v;
        if(vis[v] || v == fa) continue;
        get_root(v,u);
        sz[u] += sz[v];
        mx[u] = max(mx[u], sz[v]);
    }
    mx[u] = max(mx[u], S - mx[u]);
    if(mx[u] < mx[root]) root = u;
}
int dd[N],tot;
void get_dis(int u,int fa,int d){
    for(auto i:e[u]){
        int v = i.v, w = i.w;
        if(v == fa || vis[v]) continue;
        dd[++tot] = d + w;
        get_dis(v,u,dd[tot]);
    }
}
ll calc(int u,int fa,int d){
    tot = 1;
    dd[tot] = d;
    get_dis(u,fa,d);
    sort(dd+1,dd+1+tot);
    ll res = 0;
    for(int i = 1; i <= tot; i++){
        if(dd[i] > k) break;
        int l = i + 1;
        int r = upper_bound(dd + 1 + i,dd + 1 + tot,k - dd[i]) - dd;
        res += r - l; 
    }
    return res;
}
ll ans = 0;
void dfz(int u,int fa){
    vis[u] = 1;
    ans += calc(u,fa,0);
    for(auto i:e[u]){
        int v = i.v;
        if(vis[v] || v == fa) continue;
        ans -= calc(v,u,i.w);
        root = 0;
        mx[root] = INT_MAX;
        S = sz[v];
        get_root(v,u);
        get_root(root,-1);
        dfz(root,-1);
    }
}
int main()
{
    IOS
    cin >> n;
    rep(i,1,n-1){
        int u,v,w;
        cin >> u >> v >> w;
        e[u].push_back({v,w});
        e[v].push_back({u,w});
    }
    cin >> k;
    S = n;
    root = 0;
    mx[root] = INT_MAX;
    get_root(1,-1);
    get_root(root,-1);
    //cout << root << endl;
    dfz(root,-1);
    cout << ans;
    return 0;
}

四、P2634 [国家集训队]聪聪可可

**思路:**记录答案时,模3处理就行
code:

/*
 * @Author: 0iq_love_zy
 * @LastEditTime: 2021-02-24 10:07:44
 * @CSDN blog: https://blog.csdn.net/acm_durante
 * @E-mail: 1055323152@qq.com
 * @ProbTitle: 
 */
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define lowbit(x) ((x) & -(x))
#define lson p << 1 , l, mid 
#define rson p << 1 | 1, mid + 1, r
#define mem(a, b) memset(a, b, sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const double PI = acos(-1.0);
const int N = 2e4+50;
struct node{
    int v,w;
};
vector<node>e[N];
int n,S,root,sz[N],mx[N];
bool vis[N];

void get_root(int u,int fa){
    mx[u] = 0;
    sz[u] = 1;
    for(auto i:e[u]){
        int v = i.v;
        if(vis[v] || v == fa) continue;
        get_root(v,u);
        sz[u] += sz[v];
        mx[u] = max(mx[u],sz[v]);
    }
    mx[u] = max(mx[u],S - mx[u]);
    if(mx[u] < mx[root]) root = u;
}
int dd[N],tot;
ll ans;

void get_dis(int u,int fa,int di){
    for(auto i:e[u]){
        int v = i.v, w = i.w;
        if(vis[v] || v == fa) continue;
        dd[++tot] = (di + w) % 3;
        get_dis(v,u,dd[tot]);
    }
}
ll cnt[4];
ll calc(int u,int fa,int di){
    dd[tot = 1] = di % 3;
    get_dis(u,fa,di);
    sort(dd+1,dd+1+tot);
    mem(cnt,0);
    ll res = 0;
    for(int i = 1; i <= tot ; i++){
        cnt[dd[i]]++;
    }
    //cout << "u = " << u << " fa = " << fa << endl;
    res += cnt[0] * cnt[0];
    res += cnt[2] * cnt[1] * 2;
    /*rep(i,0,2) cout << cnt[i] << " ";
    cout << endl;*/
    return res;
}
void dfz(int u,int fa){
    vis[u] = 1;
    ans += calc(u,fa,0);
    for(auto i:e[u]){
        int v = i.v, w = i.w;
        if(vis[v] || v == fa) continue;
        ans -= calc(v,u,w);
        root = 0,mx[0] = INT_MAX,S = sz[v];
        get_root(v,u);
        get_root(root,-1);
        dfz(root,-1);
    } 
} 
int main()
{
    IOS
    cin >> n;
    rep(i,2,n){
        int u,v,w;
        cin >> u >> v >> w;
        e[u].push_back({v,w});
        e[v].push_back({u,w});
    }
    S = n,root = 0,mx[0] = INT_MAX; 
    get_root(1,-1);
    get_root(root,-1);
    dfz(root,-1);
    ll ans2 = n * n;
    ll tmp = __gcd(ans,ans2);
    cout << ans/tmp << "/" << ans2/tmp;
    return 0;
}
/*
5
1 2 1
1 3 2
1 4 1
2 5 3
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值