首页 > 其他 > 详细

树分治专题(学习笔记+做题记录)

时间:2019-03-06 14:35:00      阅读:191      评论:0      收藏:0      [点我收藏+]

点分治

  点分治可以用来处理有关树上路径的问题

  首先选取当前子树的重心作为分治点,因为重心可保证最大的子树不超过(u/2),这样每次递归的处理下去,复杂度是(nlogn)的

  求重心代码:

技术分享图片
void getroot(int u,int par){
    sz[u]=1,son[u]=0;
    for(int i=h[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(vis[v] || v==par) continue;
        getroot(v,u);
        sz[u]+=sz[v];
        son[u]=max(son[u],sz[v]); 
    }
    son[u]=max(son[u],sum-sz[u]);
    if(son[u]<maxson){
        maxson=son[u];
        rt=u;
    }
}
View Code

 

  

题目:

IOI2011 Race

  题意:给定一棵树,每条边有边权,求一条路径,权值和等于k,且边的数量最小

  solution:

  记录s[i],当前点到分治中心的边数,d[i]当前点到分治中心的距离,f[i]已统计完的子树中,距分治中心距离为 i 的路径中的最小边数

  处理当前子树时,直接用f数组更新答案

  每次统计完一棵子树的答案之后,更新f数组

  这样既可以考虑到所有的路径,又避免重复统计答案

  

技术分享图片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
using namespace std;
typedef long long ll;

const int maxn = 1000010;
const int INF = 1e9+7;

int n,k,ans=INF;
int sum,maxs;

int h[maxn],size;
struct E{
    int to,next,cost;
}e[maxn<<1];
void add(int u,int v,int w){
    e[++size].to=v;
    e[size].next=h[u];
    e[size].cost=w;
    h[u]=size;
}

int rt;
int vis[maxn],sz[maxn],son[maxn];
int d[maxn],s[maxn],f[maxn*10],c[maxn],ton[maxn*10],tot,cnt;

void getroot(int u,int par){
    sz[u]=1,son[u]=0;
    for(int i=h[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(v==par||vis[v]) continue;
        getroot(v,u);
        sz[u]+=sz[v];
        son[u]=max(son[u],sz[v]);
    }
    son[u]=max(son[u],sum-sz[u]);
    if(son[u]<maxs){
        maxs=son[u];
        rt=u;
    }
}

void dfs(int u,int par,int chang){
    d[u]=d[par]+chang;
    if(d[u]<=k) ton[++tot]=d[u];
    s[u]=s[par]+1;
    c[++cnt]=u;
    if(k>=d[u]) ans=min(ans,f[k-d[u]]+s[u]);
    for(int i=h[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(vis[v]||v==par) continue;
        dfs(v,u,e[i].cost);
    }
}

void solve(int u){
    vis[u]=1;
    tot=0; f[0]=0; d[u]=0; s[u]=0;
    for(int i=h[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(vis[v]) continue;
        cnt=0; 
        dfs(v,u,e[i].cost);
        
        for(int j=1;j<=cnt;++j){
            if(d[c[j]]>k) continue;
            f[d[c[j]]]=min(f[d[c[j]]],s[c[j]]);
        }
    }
    
    for(int i=1;i<=tot;++i) f[ton[i]]=INF;
    
    for(int i=h[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(vis[v]) continue;
        sum=sz[v],maxs=INF,rt=0;
        getroot(v,0);
        solve(rt);
    }
}

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<0 || ch>9){ if(ch==-) f=-1; ch=getchar(); } while(ch>=0 && ch<=9){ s=s*10+ch-0; ch=getchar(); } return s*f;}

int main(){
    memset(h,-1,sizeof(h));
    n=read(),k=read();
    int u,v; ll w;
    for(int i=1;i<=1000000;++i) f[i]=INF;
    for(int i=1;i<n;++i){
        u=read(),v=read(),w=read();
        u++,v++;
        add(u,v,w),add(v,u,w);
    }
    
    sum=n; maxs=INF;
    getroot(1,0);
    
    solve(rt);
    
    printf("%d\n",ans==INF?-1:ans);
    
    return 0;
}
View Code

 

树分治专题(学习笔记+做题记录)

原文:https://www.cnblogs.com/tuchen/p/10482916.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!