【模板】IOI2011race-点分治

题意

给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小
N200000,K1000000 N ≤ 200000 , K ≤ 1000000


代码

点分治的一类经典模型

#include
#include
#include
using namespace std;
const int N=2e5+10,inf=2e9,K=1e6+10;
int n,k,ans=inf,sz[N],S,MX,rt,vis[N],mx[N],ct[K];
int head[N],to[N<<2],nxt[N<<2],w[N<<2],tot,dis[N],d[N];

inline int rd()
{
    char ch=getchar();int x=0,f=1;
    while(!isdigit(ch)){if(ch==-'-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}

inline void lk(int u,int v,int c)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=c;}

inline void getrt(int x,int fa)
{
    register int i,j;
    sz[x]=1;mx[x]=0;
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(j==fa || vis[j]) continue;
        getrt(j,x);
        sz[x]+=sz[j];
        mx[x]=max(mx[x],sz[j]);
    }
    mx[x]=max(mx[x],S-sz[x]);
    if(mx[x]inline void cal(int x,int fa)
{
    register int i,j;
    if(dis[x]<=k) ans=min(ans,ct[k-dis[x]]+d[x]);
    for(i=head[x];i;i=nxt[i]){
        j=to[i];
        if(j==fa || vis[j]) continue;
        d[j]=d[x]+1;dis[j]=dis[x]+w[i];
        cal(j,x);
    }
}

inline void update(int x,int fa)
{
    if(dis[x]<=k) ct[dis[x]]=min(ct[dis[x]],d[x]);
    for(register int j,i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j] || j==fa)continue;
        update(j,x);
    }
}

inline void del(int x,int fa)
{
    if(dis[x]<=k) ct[dis[x]]=inf;
    for(register int j,i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j] || j==fa)continue;
        del(j,x);
    }
}

inline void divide(int x)
{
    register int i,j;
    vis[x]=1;ct[0]=0;
    for(i=head[x];i;i=nxt[i]){
        j=to[i];
        if(vis[j]) continue;
        dis[j]=w[i];d[j]=1;
        cal(j,x);update(j,x);
    }
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j]) continue;
        del(j,x);
    }
    for(i=head[x];i;i=nxt[i]){
        j=to[i];if(vis[j]) continue;
        S=sz[j];MX=inf;rt=0;
        getrt(j,x);
        divide(rt);
    }
}

int main(){
    register int i,j,ix,iy,iz;
    n=rd();k=rd();
    for(i=0;i<=k;++i) ct[i]=inf;
    for(i=1;i1;iy=rd()+1;iz=rd();lk(ix,iy,iz);lk(iy,ix,iz);}
    MX=inf;S=n;getrt(1,0);divide(rt);
    if(ansprintf("%d\n",ans);else printf("-1\n");
}

你可能感兴趣的:(点分治)