[POJ 1741] DP + Tree 分治

题意:

给一棵树,有边权。问边权和<=k的路径有多少条。

解法:

TreeDp+分治。漆子超的论文。。

const int N = 1e4 + 9;
int n , k , ans;
struct EDGE{
    int to , len , next;
}edge[N << 1];
int head[N] , tot;
void addedge(int u , int v , int len){
    edge[tot].to = v;
    edge[tot].len = len;
    edge[tot].next = head[u];
    head[u] = tot++;
}
int sons[N] , core , corecnt;
int dis[N];
int lis[N] , listot;
bool vis[N];
void coredfs(int u , int v){    // Count the Sons
    sons[u] = 1;
    int ret = 1;
    for(int i = head[u] ; i != -1 ; i = edge[i].next){
        int go = edge[i].to;
        if (go == v || vis[go]) continue;
        coredfs(go , u);
        sons[u] += sons[go];
    }
}
void coreedit(int u , int v , int cnt){ //Find core
    int ret = 1;
    for(int i = head[u] ; i != -1 ; i = edge[i].next){
        int go = edge[i].to;
        if (go == v || vis[go]) continue;
        coreedit(go , u , cnt);
        checkMax(ret , sons[go]);
    }
    checkMax(ret , cnt - sons[u]);

    if (ret < corecnt){
        core = u;
        corecnt = ret;
    }
}
int Qcore(int u){       // Subtree's Core
    corecnt = INF;
    coredfs(u , 0);
    coreedit(u , 0 , sons[u]);
    return core;
}
void rushDis(int u , int len , int v){
    lis[listot++] = len;
    for (int i = head[u] ; i != -1 ; i = edge[i].next){
        int g = edge[i].to;
        if (g != v && !vis[g]) rushDis(g , len + edge[i].len , u);
    }
}
int calc(int u , int len){
    int ret = 0;
    listot = 0;
    rushDis(u , len , 0);
    sort(lis , lis + listot);
    int head = 0 , tail = listot - 1;
    while(head < tail){
        while(head < tail && lis[head] + lis[tail] > k) tail--;
        ret += tail - head;
        head++;
    }
    return ret;
}
void dfs(int u){    //Treedp   the subtree of Root-u
    Qcore(u);
    ans += calc(core , 0);
    vis[core] = 1;
    for (int i = head[core] ; i != -1 ; i = edge[i].next){
        int g = edge[i].to;
        if (!vis[g]){
            ans -= calc(g , edge[i].len);
            dfs(g);
        }
    }
}
void solve(){
    tot = 0;
    FLC(head , -1);
    REP(i , n - 1){
        int u , v , w;
        RD(u , v , w);
        addedge(u , v , w);
        addedge(v , u , w);
    }
    ans = 0;
    RST(vis);
    dfs(1);
    OT(ans);
}
int main(){
//    freopen("0.txt" , "r" , stdin);
//    freopen("1.txt" , "w" , stdout);
    while(RD(n , k) , n && k) solve();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值