zoukankan      html  css  js  c++  java
  • 【洛谷4149】[IOI2011] Race(点分治)

    点此看题面

    大致题意: 给你一棵树,问长度为\(K\)的路径至少由几条边构成。

    点分治

    这题应该比较显然是点分治

    主要思路

    与常见的点分治套路一样,由于\(K≤1000000\),因此我们可以考虑开个桶\(f\)数组来记录每种长度的路径至少由几条边构成

    但是要注意,每换一个根要将桶清空

    呃,暴力清空肯定\(T\)飞。

    于是就需要再开一个\(g\)数组,记录每个答案是在以哪一个节点为根时求出来的,这样就可以避免清空数组了。这也是一个比较常用的套路。

    其余的过程与点分板子差不多,就不多说了。

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define uint unsigned int
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define abs(x) ((x)<0?-(x):(x))
    #define INF 1e9
    #define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
    #define ten(x) (((x)<<3)+((x)<<1))
    #define N 200000
    #define K 1000000
    #define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=z)
    using namespace std;
    int n,k,ee=0,lnk[N+5];
    struct edge
    {
        int to,nxt,val;
    }e[2*N+5];
    class FIO
    {
        private:
            #define Fsize 100000
            #define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
            #define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
            int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
        public:
            FIO() {FinNow=FinEnd=Fin;}
            inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
            inline void read_char(char &x) {while(isspace(x=tc()));}
            inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
            inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
            inline void write_char(char x) {pc(x);}
            inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
            inline void end() {fwrite(Fout,1,FoutSize,stdout);}
    }F;
    class Class_DotSolver//点分治
    {
        private:
            int ans,rt,top,f[K+5],g[K+5],used[N+5],Size[N+5],Max[N+5],dis[N+5],len[N+5],Stack[N+5];
            inline void GetRt(int x,int lst,int sum)//找重心做根
            {	
                register int i;
                for(i=lnk[x],Size[x]=1,Max[x]=0;i;i=e[i].nxt)
                    if(e[i].to^lst&&!used[e[i].to]) GetRt(e[i].to,x,sum),Size[x]+=Size[e[i].to],Max[x]=max(Max[x],Size[e[i].to]);
                if((Max[x]=max(Max[x],sum-Size[x]))<Max[rt]) rt=x;
            }
            inline void dfs(int x,int lst)//遍历子树,将各种路径保存下来
            {
                register int i;
                for(i=lnk[Stack[++top]=x];i;i=e[i].nxt)
                    if(e[i].to^lst&&!used[e[i].to]) dis[e[i].to]=dis[x]+e[i].val,len[e[i].to]=len[x]+1,dfs(e[i].to,x);
            }
            inline void Solve(int x)//在以x为根的子树中求解
            {
                register int i,j;
                for(i=lnk[x],used[x]=1,g[0]=x;i;i=e[i].nxt) 
                {
                    if(used[e[i].to]) continue;
                    dis[e[i].to]=e[i].val,len[e[i].to]=1,dfs(e[i].to,x);//遍历该子树
                    for(j=top;j;--j) if(dis[Stack[j]]<=k&&!(g[k-dis[Stack[j]]]^x)) ans=min(ans,len[Stack[j]]+f[k-dis[Stack[j]]]);//更新答案
                    while(top) 
                    {	
                        if(dis[Stack[top]]<=k) g[dis[Stack[top]]]^x?(g[dis[Stack[top]]]=x,f[dis[Stack[top]]]=len[Stack[top]]):(f[dis[Stack[top]]]=min(f[dis[Stack[top]]],len[Stack[top]]));//更新桶
                        --top;
                    }
                }
                for(i=lnk[x];i;i=e[i].nxt) if(!used[e[i].to]) GetRt(e[i].to,rt=0,Size[e[i].to]),Solve(rt);//继续对子树进行处理
            }
        public:
            inline int GetAns() {return (void)(ans=Max[0]=INF,GetRt(1,rt=0,n),Solve(rt)),ans==INF?-1:ans;}//求答案
    }DotSolver;
    int main()
    {
        register int i,x,y,z;
        for(F.read(n),F.read(k),i=1;i<n;++i) F.read(x),F.read(y),F.read(z),++x,++y,add(x,y,z),add(y,x,z);//注意细节,是从0开始编号的,所以将x和y各加1
        return F.write(DotSolver.GetAns()),F.end(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    c++之运算符
    C++开源库,欢迎补充。
    C++ 图像处理类库
    C++开源代码项目汇总
    视频会议及流媒体十大开源项目
    多媒体的框架
    C++开发资源汇总
    Juce之旅-第一个例子(图形窗口)
    Juce-强大的开源类库
    图像处理库的比较:OpenCV,FreeImage,CImg,CxImage
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4149.html
Copyright © 2011-2022 走看看