2014西安网络预选赛1009(点分治)HDU5016

Mart Master II

Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 167    Accepted Submission(s): 63


Problem Description
Trader Dogy lives in city S, which consists of n districts. There are n - 1 bidirectional roads in city S, each connects a pair of districts. Indeed, city S is connected, i.e. people can travel between every pair of districts by roads.

In some districts there are marts founded by Dogy’s competitors. when people go to marts, they’ll choose the nearest one. In cases there are more than one nearest marts, they’ll choose the one with minimal city number.

Dogy’s money could support him to build only  one new marts, he wants to attract as many people as possible, that is, to build his marts in some way that maximize the number of people who will choose his mart as favorite. Could you help him?
 

Input
There are multiple test cases. Please process till EOF.

In each test case: 

First line: an integer n indicating the number of districts.

Next n - 1 lines: each contains three numbers b i, e i and w i, (1 ≤ b i,e i ≤ n,1 ≤ w i ≤ 10000), indicates that there’s one road connecting city b i and e i, and its length is w i.

Last line : n(1 ≤ n ≤ 10 5) numbers, each number is either 0 or 1, i-th number is 1 indicates that the i-th district has mart in the beginning and vice versa.
 

Output
For each test case, output one number, denotes the number of people you can attract, taking district as a unit.
 

Sample Input
   
   
5 1 2 1 2 3 1 3 4 1 4 5 1 1 0 0 0 1 5 1 2 1 2 3 1 3 4 1 4 5 1 1 0 0 0 0 1 1 1 0
 

Sample Output
   
   
2 4 0 1

题意:给出n个点的树,有些点已经有了市场,而每个点都会被它最近的市场统治,现在要找一个点建市场,使得能统治最多的点

思路:又是一道树分治类型的题目,这种题目一直是难点也是热点吧

            首先要预处理,即求出每个点被哪些市场统治了,可以用pair来存,代码里为du[]数组,第一维是距离,第二维是被哪个点统治,这个用树形DP很容易求得

            然后就是树分治的部分

            对于每一个分治中心C,要求的就是点u能不能通过C被v统治

            可以先一遍DFS扫一遍所有点,然后如果du[u]>=dis[u]则将该点加入vector,其中dis[u]是u与分治中心C的距离

            然后就可以一个分支一个分支的求了,对一个分支里面的每个点可以通过在vector上二分找到能统治的所有点的数量

            但是会算重复,因为它会将该条分支满足的点也算进去

            所以在求之前要先在这条分支上扫一遍所有点,将满足上述公式的点加入另一个vector

            然后算的时候只需要用总的减去该条分支的即可

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         #include 
        
          #include 
         
           #include 
           using namespace std; typedef pair 
           
             pi; const int MAXN = 100010; const int INF = ~0U>>1; int head[MAXN]; int edge[MAXN<<1]; int next[MAXN<<1]; int ww[MAXN<<1]; int is[MAXN]; int num[MAXN]; int cnum[MAXN]; int vis[MAXN]; int ans[MAXN]; int tot; int center; int amx; int esz; pi du[MAXN]; void init() { memset(vis,0,sizeof(vis)); memset(ans,0,sizeof(ans)); memset(is,0,sizeof(is)); memset(head,-1,sizeof(head)); memset(du,0,sizeof(du)); esz=0; } void addedge(int u,int v,int w) { edge[esz]=v; ww[esz]=w; next[esz]=head[u]; head[u]=esz++; } void getdu(int u,int p) { du[u] = is[u] ? make_pair(0,u):make_pair(INF,INF); for(int i=head[u];i!=-1;i=next[i]){ int v=edge[i]; if(v==p)continue; getdu(v,u); pi pii=make_pair(du[v].first==INF?du[v].first:du[v].first+ww[i],du[v].second); if(pii 
            
              cnum[u])cnum[u]=num[v]; } } void getcenter(int u,int p) { int mx=max(tot-num[u],cnum[u]); if(mx 
             
               all; vector 
              
                sub; int dis[MAXN]; void dfs1(int u,int p,int w) { dis[u]=dis[p]+w; if(dis[u]<=du[u].first)all.push_back(make_pair(du[u].first-dis[u],du[u].second)); for(int i=head[u];i!=-1;i=next[i]){ int v=edge[i]; if(v==p || vis[v])continue; dfs1(v,u,ww[i]); } } void dfs2(int u,int p) { if(dis[u]<=du[u].first)sub.push_back(make_pair(du[u].first-dis[u],du[u].second)); for(int i=head[u];i!=-1;i=next[i]){ int v=edge[i]; if(v==p || vis[v])continue; dfs2(v,u); } } int fx(int u) { return all.end()-lower_bound(all.begin(),all.end(),make_pair(dis[u],u)); } int gx(int u) { return sub.end()-lower_bound(sub.begin(),sub.end(),make_pair(dis[u],u)); } void dfs3(int u,int p) { ans[u]+=fx(u)-gx(u); for(int i=head[u];i!=-1;i=next[i]){ int v=edge[i]; if(v==p || vis[v])continue; dfs3(v,u); } } void solve(int u) { center=u; getnum(u,0); tot=num[u]; amx=tot-1; getcenter(u,0); dis[center]=0; all.clear(); dfs1(center,0,0); sort(all.begin(),all.end()); ans[center]+=fx(center); for(int i=head[center];i!=-1;i=next[i]){ int v=edge[i]; if(vis[v])continue; sub.clear(); dfs2(v,center); sort(sub.begin(),sub.end()); dfs3(v,center); } vis[center]=1; for(int i=head[center];i!=-1;i=next[i]){ int v=edge[i]; if(vis[v])continue; solve(v); } } int main() { int n; while(scanf("%d",&n)!=EOF){ init(); for(int i=1;i 
               
                 an)an=ans[i]; } printf("%d\n",an); } return 0; } 
                
               
              
             
            
          
         
       
      
      
     
     
    
    
   
   
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值