[IOI2011] Race [点分治]

题面:

https://www.luogu.org/problemnew/show/P4149

bzoj恶心,这都放权限......

思路:

统计树上路径信息,点分治啊

K<=1e6,考虑对于距离为i的点建立tmp[i],表示在当前递归到的子树中,走到距离为i的顶点最少需要走多少边

点分治,每次先对每棵子树遍历,求出每个点i到root的距离dis[i],以及走过的边数d[i]

那么ans=min(ans,tmp[k-dis[i]]+d[i])

遍历完这棵子树再修改被访问了的tmp[dis[i]],然后下一棵

所有子树遍历完了以后,再遍历一遍所有节点,把修改到的tmp值变回inf(初始就是inf)

然后才对每一个子树找重心、递归下一步操作

注意:

tmp要开到1000000,不要开成n的大小

每次进入dfs_solve时tmp[0]=0,因为这个当前的根到自己距离为0,走过了0条边

注意常数优化

Code:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cstdlib>
  6 #define inf 1e9
  7 using namespace std;
  8 inline int read(){
  9     int re=0,flag=1;char ch=getchar();
 10     while(ch>'9'||ch<'0'){
 11         if(ch=='-') flag=-1;
 12         ch=getchar();
 13     }
 14     while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
 15     return re*flag;
 16 }
 17 int n,m,cnt,sum,root,first[200010],siz[200010],son[200010];
 18 int d[200010],dis[200010],ans=inf,tmp[1000010];
 19 bool vis[200010];
 20 struct edge{
 21     int to,next,w;
 22 }a[400010];
 23 inline void add(int u,int v,int w){
 24     a[++cnt]=(edge){v,first[u],w};first[u]=cnt;
 25     a[++cnt]=(edge){u,first[v],w};first[v]=cnt;
 26 }
 27 inline int _max(int l,int r){return (l>r)?l:r;}
 28 inline int _min(int l,int r){return (l<r)?l:r;}
 29 void getroot(int u,int f){
 30     int i,v;
 31     siz[u]=1;son[u]=0;
 32     for(i=first[u];~i;i=a[i].next){
 33         v=a[i].to;
 34         if(v==f||vis[v]) continue;
 35         getroot(v,u);
 36         siz[u]+=siz[v];son[u]=_max(son[u],siz[v]);
 37     }
 38     son[u]=_max(son[u],sum-siz[u]);
 39     if(son[u]<son[root]) root=u;
 40 }
 41 void calc(int u,int f){
 42     int i,v;
 43     if(dis[u]<=m) ans=_min(ans,tmp[m-dis[u]]+d[u]);
 44     for(i=first[u];~i;i=a[i].next){
 45         v=a[i].to;
 46         if(v==f||vis[v]) continue;
 47         dis[v]=dis[u]+a[i].w;d[v]=d[u]+1;
 48         calc(v,u);
 49     }
 50 }
 51 void update(int u,int f,int t){
 52     int i,v;
 53     if(dis[u]<=m){
 54         if(t) tmp[dis[u]]=_min(tmp[dis[u]],d[u]);
 55         else tmp[dis[u]]=inf;
 56     }
 57     for(i=first[u];~i;i=a[i].next){
 58         v=a[i].to;
 59         if(v==f||vis[v]) continue;
 60         update(v,u,t);
 61     }
 62 }
 63 void dfs(int u){
 64     int i,v;vis[u]=1;tmp[0]=0;//注意这里!!!!!!!!!
 65     for(i=first[u];~i;i=a[i].next){
 66         v=a[i].to;
 67         if(vis[v]) continue;
 68         dis[v]=a[i].w;d[v]=1;
 69         calc(v,0);update(v,0,1);
 70     }
 71     for(i=first[u];~i;i=a[i].next) if(!vis[a[i].to]) update(a[i].to,0,0);
 72     for(i=first[u];~i;i=a[i].next){
 73         v=a[i].to;
 74         if(vis[v]) continue;
 75         sum=siz[v];root=0;
 76         getroot(v,0);
 77         dfs(root);
 78     }
 79 }
 80 int main(){
 81 
 82     int size = 128 << 20;
 83     char *p = (char*)malloc(size) + size;  
 84     __asm__("movl %0, %%esp
" :: "r"(p));//有些OJ上不扩栈会RE
 85 
 86     freopen("ioi2011-race.in","r",stdin);
 87     freopen("ioi2011-race.out","w",stdout);
 88 
 89     memset(first,-1,sizeof(first));
 90     int i,t1,t2,t3;
 91     n=read();m=read();
 92     for(i=1;i<=m;i++) tmp[i]=inf;
 93     for(i=1;i<n;i++){
 94         t1=read();t2=read();t3=read();//点是从零开始的
 95         add(t1+1,t2+1,t3);
 96     }
 97     sum=n;root=0;son[0]=n;
 98     getroot(1,0);
 99     dfs(root);
100     if(ans!=inf) printf("%d
",ans);
101     else printf("-1");
102 }
原文地址:https://www.cnblogs.com/dedicatus545/p/8401118.html