zoukankan      html  css  js  c++  java
  • BZOJ 2599: [IOI2011]Race (点分治)

    题目描述

    给一棵树,每条边有权。求一条简单路径,权值和等于$K$ ,且边的数量最小。

    输入输出格式

    输入格式:

    第一行:两个整数$n,k$

    第二至$n$行:每行三个整数,表示一条无向边的两端和权值 (注意点的编号从$n$开始)。

    输出格式:

    一个整数,表示最小边数量。

    如果不存在这样的路径,输出 -11 。

    输入输出样例

    输入样例#1: 复制
    4 3
    0 1 1
    1 2 2
    1 3 4
    输出样例#1: 复制
    2

    说明

    $nle 200000,Kle 1000000$

    题解

      我们用一个数组$f[j]$表示点分到当前子树中时路径长度为$j$的最小边数

      然后每一棵子树里去$dfs$一遍更新$f$数组,然后用$f$数组相加更新答案

      复杂度为$O(nlogn)$

     1 //minamoto
     2 #include<cstdio>
     3 #include<iostream>
     4 #define ll long long
     5 using namespace std;
     6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
     7 char buf[1<<21],*p1=buf,*p2=buf;
     8 template<class T>inline int cmax(T&a,const T&b){return a<b?a=b,1:0;}
     9 template<class T>inline int cmin(T&a,const T&b){return a>b?a=b,1:0;}
    10 inline int read(){
    11     #define num ch-'0'
    12     char ch;bool flag=0;int res;
    13     while(!isdigit(ch=getc()))
    14     (ch=='-')&&(flag=true);
    15     for(res=num;isdigit(ch=getc());res=res*10+num);
    16     (flag)&&(res=-res);
    17     #undef num
    18     return res;
    19 }
    20 char sr[1<<21],z[20];int C=-1,Z;
    21 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    22 inline void print(int x){
    23     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    24     while(z[++Z]=x%10+48,x/=10);
    25     while(sr[++C]=z[Z],--Z);sr[++C]='
    ';
    26 }
    27 const int N=200005,K=1000005;
    28 int ver[N<<1],head[N],Next[N<<1],edge[N<<1];
    29 int n,k,ans,tot,size,sz[N],son[N],rt,cnt[K];bool vis[N];ll d[N];
    30 inline void add(int u,int v,int e){
    31     ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
    32     ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=e;
    33 }
    34 void findrt(int u,int fa){
    35     sz[u]=1,son[u]=0;
    36     for(int i=head[u];i;i=Next[i]){
    37         int v=ver[i];if(v==fa||vis[v]) continue;
    38         findrt(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]);
    39     }
    40     cmax(son[u],size-sz[u]);
    41     if(son[u]<son[rt]) rt=u;
    42 }
    43 void dfs(int u,int fa,int dep){
    44     if(d[u]>=0&&d[u]<=k) cmin(ans,dep+cnt[k-d[u]]);
    45     for(int i=head[u];i;i=Next[i]){
    46         int v=ver[i];
    47         if(v!=fa&&!vis[v]){
    48             d[v]=d[u]+edge[i],dfs(v,u,dep+1);
    49         }
    50     }
    51 }
    52 void update(int u,int fa,int dep,int opt){
    53     if(d[u]>=0&&d[u]<=k)
    54     opt?cmin(cnt[d[u]],dep):cnt[d[u]]=n;
    55     for(int i=head[u];i;i=Next[i]){
    56         int v=ver[i];
    57         if(!vis[v]&&v!=fa)
    58         update(v,u,dep+1,opt);
    59     }
    60 }
    61 void solve(int u){
    62     vis[u]=true,cnt[0]=0;
    63     for(int i=head[u];i;i=Next[i]){
    64         int v=ver[i];
    65         if(!vis[v])
    66         d[v]=edge[i],dfs(v,0,1),update(v,0,1,1);
    67     }
    68     for(int i=head[u];i;i=Next[i]){
    69         int v=ver[i];
    70         if(!vis[v])
    71         update(v,0,1,0);
    72     }
    73     int totsz=size;
    74     for(int i=head[u];i;i=Next[i]){
    75         int v=ver[i];
    76         if(!vis[v]){
    77             rt=0,size=sz[v];
    78             findrt(v,0),solve(rt);
    79         }
    80     }
    81 }
    82 int main(){
    83     n=read(),k=read(),ans=n;
    84     for(int i=1;i<n;++i){
    85         int u=read()+1,v=read()+1,e=read();add(u,v,e);
    86     }
    87     for(int i=1;i<=k;++i) cnt[i]=n;
    88     son[rt=0]=n+1,size=n,findrt(1,0),solve(rt);
    89     print(ans==n?-1:ans);
    90     Ot();
    91     return 0;
    92 }
  • 相关阅读:
    HDU 5919 分块做法
    HDU 3333 分块求区间不同数和
    CF 333E 计算几何+bitset优化
    hdu 1043 八数码--打表
    hdu 1043 八数码问题-A*搜索
    hdu 5919 主席树
    hiho1388 FFT/NTT
    HDU 5869区间CGD不同种类数---树状数组+map统计区间不同种类数(离线)
    HDU 5875 二分+st表
    HDU 5898 基础数位DP
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9487682.html
Copyright © 2011-2022 走看看