P3806树上点分治

链接:https://www.luogu.org/problemnew/show/P3806

我学习点分治的模板:https://www.cnblogs.com/Paul-Guderian/p/6782671.html

题目描述

给定一棵有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

// luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M = 10005, K = 10000001;

int n, tot, cnt, Ans, root, sum, h[M], siz[M], dis[M], tong[M], f[M], num[K+1];
bool vis[M];
struct edge{int v, nxt, w;}G[M<<1];
void add(int u, int v, int w){
    G[++tot].v = v; G[tot].w = w; G[tot].nxt = h[u]; h[u] = tot;
}
void init(){
    memset(vis, 0, sizeof(vis));
    memset(h, 0, sizeof(h));
    tot = 0; Ans = 0; sum = 0;
}


void getdeep(int u, int fa){
    tong[++cnt] = dis[u];
    for(int i = h[u]; i; i = G[i].nxt){
        int v = G[i].v;
        if(vis[v] || v == fa)continue;
        dis[v] = dis[u] + G[i].w;    
        getdeep(v, u);    
    }
}

void getroot(int u, int fa){
    siz[u] = 1; f[u] = 0;
    for(int i = h[u]; i; i = G[i].nxt){
        int v = G[i].v;
        if(vis[v] || v == fa)continue;
        getroot(v, u);
        siz[u] += siz[v];
        f[u] = max(f[u], siz[v]);
    }
    f[u] = max(f[u], sum - siz[u]);
    if(f[u] < f[root]) root = u; 
    
}
void cal(int u, int ret, bool opt){
    dis[u] = ret;
    cnt = 0; 
    getdeep(u, 0);
    int ans = 0;
    for(int i = 1; i <= cnt; i++)
        for(int j = i + 1; j <= cnt; j++){
            if(opt && tong[i] + tong[j] < K) ++num[tong[i] + tong[j]];
            else if(tong[i] + tong[j] < K) --num[tong[i] + tong[j]];    
        }
}

void solve (int u){
    cal(u, 0, 1);
    vis[u] = 1;
    for(int i = h[u]; i; i = G[i].nxt){
        int v = G[i].v;
        if(vis[v])continue;
        cal(v, G[i].w, 0);
        sum = siz[v];
        getroot(v, root = 0);
        solve(root);
    }
}


int main(){
    f[0] = 1e9;
    int m, fg = 0;
    scanf("%d%d", &n, &m);
    for(int i = 1; i < n; i++){
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        add(u, v, w); add(v, u, w);
        if(!w)fg = 1;
    }
        
    solve(1);
    num[0] = fg;
    while(m--){
        int k; scanf("%d", &k);
        if(num[k])puts("AYE");
        else puts("NAY");
    }
}
View Code

P4178 Tree

链接:https://www.luogu.org/problemnew/show/P4178

题目描述

给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K

输入输出格式

输入格式:

N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k

输出格式:

一行,有多少对点之间的距离小于等于k

输入输出样例

输入样例#1: 复制
7
1 6 13 
6 3 9 
3 5 7 
4 1 3 
2 4 20 
4 7 2 
10
输出样例#1: 复制
5
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M = 40005;

int n, k, tot, cnt, Ans, root, sum, h[M], siz[M], dis[M], tong[M], f[M];
bool vis[M];
struct edge{int v, nxt, w;}G[M<<1];
void add(int u, int v, int w){
    G[++tot].v = v; G[tot].w = w; G[tot].nxt = h[u]; h[u] = tot;
}
void init(){
    memset(vis, 0, sizeof(vis));
    memset(h, 0, sizeof(h));
    tot = 0; Ans = 0; sum = 0;
}


void getdeep(int u, int fa){
    tong[++cnt] = dis[u];
    for(int i = h[u]; i; i = G[i].nxt){
        int v = G[i].v;
        if(vis[v] || v == fa)continue;
        dis[v] = dis[u] + G[i].w;    
        getdeep(v, u);    
    }
}

void getroot(int u, int fa){
    siz[u] = 1; f[u] = 0;
    for(int i = h[u]; i; i = G[i].nxt){
        int v = G[i].v;
        if(vis[v] || v == fa)continue;
        getroot(v, u);
        siz[u] += siz[v];
        f[u] = max(f[u], siz[v]);
    }
    f[u] = max(f[u], sum - siz[u]);
    if(f[u] < f[root]) root = u; 
    
}

int cal(int u, int ret){
    dis[u] = ret;
    cnt = 0; 
    getdeep(u, 0);
    int ans = 0;
    int lf = 1, rg = cnt;
    sort(tong+1, tong+1+cnt);
    while(lf < rg){
        if(tong[lf] + tong[rg] <= k) ans += rg - lf, lf++;
        else rg--;
    }
    return ans;
}

void solve (int u){
    Ans += cal(u, 0);
    vis[u] = 1;
    for(int i = h[u]; i; i = G[i].nxt){
        int v = G[i].v;
        if(vis[v])continue;
        Ans -= cal(v, G[i].w);
        sum = siz[v];
        getroot(v, root = 0);
        solve(root);
    }
}


int main(){
    f[0] = 1e9;
    scanf("%d", &n);
    init();
    for(int i = 1; i < n; i++){
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        add(u, v, w); add(v, u, w);
    }
    scanf("%d", &k);    
    solve(1);
    printf("%d
", Ans);
}
View Code
原文地址:https://www.cnblogs.com/EdSheeran/p/9395993.html