程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> POJ 1987 Distance Statistics(樹的點分治)

POJ 1987 Distance Statistics(樹的點分治)

編輯:C++入門知識

上場CF的C題是一個樹的分治。。。

今天剛好又看到一題,就做了下

題意:一棵樹,問兩個點的距離<=k的點對數目。

http://poj.org/problem?id=1987


貌似是經典的點分治題。。。。。

看成有根樹,那麼這樣的點對路徑分為兩種,1、過根節點,2、存在於某一棵子樹當中。

顯然情況2可以看成是一種子情況

對於1的統計,統計所有節點到根節點的距離,枚舉+二分可以得到有多少個二元組的和<=k。

但是需要除掉兩個點都在某一棵子樹中的情況,所以枚舉所有子樹,同樣是枚舉+二分。

至於根的選擇,選取樹的重心。。。我是兩次DFS,類似數形DP,求出所有子樹的size,找到某一個結點,若刪除這個結點,剩下的子樹的size中最大的最小。

總體復雜度大概是nlgnlgn

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#pragma comment(linker, "/STACK:1024000000,1024000000")   
using namespace std;
const int N = 40005;
struct Edge{
    int v,next,w;
}e[N<<1];
int tot,start[N],n,m,k,del[N],ans=0;
int size[N];
void _add(int u,int v,int w){
    e[tot].v=v;e[tot].w=w;
    e[tot].next=start[u];start[u]=tot++;
}
void add(int u,int v,int w){
    _add(u,v,w);
    _add(v,u,w);
}
void cal(int u,int pre){
    size[u]=1;
    for(int i=start[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v==pre||del[v]) continue;
        cal(v,u);
        size[u]+=size[v];
    }
}
int newroot,maxsize,totalsize;
void dfs(int u,int pre){
    int mx=0,sum=1;
    for(int i=start[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v==pre||del[v]) continue;
        dfs(v,u);
        mx=max(mx,size[v]);
        sum+=size[v];
    }
    mx=max(mx,totalsize-sum);
    if(mx<maxsize){
        maxsize=mx;
        newroot=u;
    }
}
int search(int r){
    newroot=-1;maxsize=1<<30;
    cal(r,-1);
    totalsize=size[r];
    dfs(r,-1);
    return newroot;
}
int dist[N],idx;
vector<int>sub[N],all;
void gao(int u,int pre){
    all.push_back(dist[u]);
    sub[idx].push_back(dist[u]);
    for(int i=start[u];i!=-1;i=e[i].next){
        int v=e[i].v,w=e[i].w;
        if(v==pre||del[v]) continue;
        dist[v]=dist[u]+w;
        gao(v,u);
    }
}
void solve(int root){
    root=search(root);
    del[root]=1;
    if(totalsize==1) return ;
    idx=0;all.clear();
    for(int i=start[root];i!=-1;i=e[i].next){
        int v=e[i].v,w=e[i].w;
        if(del[v]) continue;
        sub[idx].clear();
        dist[v]=w;
        gao(v,-1);
        sort(sub[idx].begin(),sub[idx].end());
        idx++;
    }
    for(int i=0;i<idx;i++){
        int pos;
        for(int j=0;j<sub[i].size();j++){
            pos=upper_bound(sub[i].begin(),sub[i].end(),k-sub[i][j])-sub[i].begin()-1;
            if(pos>j) ans-=pos-j;
        }
        pos=upper_bound(sub[i].begin(),sub[i].end(),k)-sub[i].begin()-1;
        if(pos>=0) ans+=pos+1;
    }
    sort(all.begin(),all.end());
    for(int i=0;i<all.size();i++){
        int pos=upper_bound(all.begin(),all.end(),k-all[i])-all.begin()-1;
        if(pos>i) ans+=pos-i;
    }
    for(int i=start[root];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(del[v]) continue;
        solve(v);
    }
}
int main(){
    tot=0;memset(start,-1,sizeof(start));
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        int u,v,w;char str[5];
        scanf("%d%d%d%s",&u,&v,&w,str);
        add(u,v,w);
    }
    scanf("%d",&k);
    solve(1);
    printf("%d\n",ans);
    return 0;
}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved