• 点分治学习笔记


    /*
    淀粉质真好吃!
    */

    P4178 Tree

    这个题概括起来就是:

    求树上两点路径小于等于K的条数

    这是点分治的传统题:维护树上的路径。

    点分治的精髓:就是不断的把一棵树拆成几棵子树来处理,并且考虑路径的合并。

    分治点的选择 :树的重心!

    为什么呢?假设树的形态是一条链,那么每次取链头的理论时间复杂度是O(n2)

    而每次取中间的时间复杂度是O(n log2 n)此时中间就是树的重心,而一棵树可以看成无数的链拼接而成,

    理应满足和链一样的性质,选重心的时候最优。可以达到近O(n log2 n)的复杂度。

    怎么求树的重心:每次找到子树中的一个点它的儿子恰好多于子树根的size[]/2

            (就是它底下的儿子恰好比子树元素的一半多一点点)

    得出这样的递归代码:

    int Get_Root(int u,int fath,int S)
    {
        int pos=0;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            if (size[v]>size[pos]) pos=v;
        }
        if (pos!=0&&size[pos]>=S/2) return Get_Root(pos,u,S);
        return u;
    }

    但是为了避免一些毒瘤题目卡树的重心,最好还是按照标准的来。

    ps: 最大子树节点个数最少的点u就是重心,这样一个树形DP就行

    void Get_Root(int u,int fath)
    {
        f[u]=0,size[u]=1;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Root(v,u);
            f[u]=max(f[u],size[v]);
            size[u]+=size[v];
        }
        f[u]=max(f[u],SIZE-size[u]);
        if (f[u]<f[root]) root=u;
    }

    记得一开始f[0]=正无穷

    然而点分治是怎么实现的呢?

    先给出一个代码:

    void solve(int u)
    {
        ans=ans+Get_Ans(u,0);
        use[u]=true;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (use[v]) continue;
            ans=ans-Get_Ans(v,a[i].w);
            int rt=Get_Root(v,u,size[v]);
            solve(rt);
        }
    }

    ans指的是一个答案,use代表这个点之前有没有计算过(一旦这个点被记做重心,那么这个点就被用了)。

    一开始加的这个ans指的是求解过u点的贡献。然后标记u的use是true

    然后找一个子树,容斥原理除去非法答案

    这里需要着重注意!!!

    这里写图片描述 

    A为重心,当前分治到A点。

    那么我们Get_Ans出来的答案应该是这么几条路径:

    A

    A-B

    A-B-C

    A-B-D

    A-E

    A-E-F

    然而我们发现这样三条路径相互叠加是没有跨越A点的!

    A-B

    A-B-C

    A-B-D

    所以这三条路径相互叠加的我们需要减去,其实很简单就是找到子树的根B(分治点的直接儿子),减去子树的单独贡献就行。

    这里需要把A-带上,增加在每一条路径前,所以就能减去非法答案了!

    然后找到一个新的分治点分治。

    对于这道题目: P4178 Tree

    其实就是Get_Ans的问题:二分左端点不断逼近,右端点不断逼近就行了。

    int Get_Ans(int u,int Len)
    {
        d[cnt=0]=0;
        Get_Dist(u,0,Len);
        sort(d+1,d+1+cnt);
        int l=1,r=cnt,an=0;
        while (l<=r) {
            if (d[l]+d[r]<=k) an=an+r-l,l++;
            else r--;
        }
        return an;
    }

    整个代码的话长这样:(size在Get_Dist中一起做掉了)

    # include <bits/stdc++.h>
    # define fp(i,s,t) for (int i=s;i<=t;i++)
    using namespace std;
    const int N=40005;
    int head[N],size[N],d[N],cnt,n,tot,ans,k;
    bool use[N];
    struct rec{int pre,to,w;}a[N<<1];
    template<typename T>void read(T &x)
    {
        x=0; int w=0; char c=0;
        while (c<'0'||c>'9') w|=c=='-',c=getchar();
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=w?-x:x;
    }
    template <typename T,typename... Args> inline void read(T& t, Args&... args)
    {
        read(t);read(args...);
    }
    void adde(int u,int v,int w)
    {
        a[++tot].pre=head[u];
        a[tot].to=v;
        a[tot].w=w;
        head[u]=tot;
    }
    int Get_Root(int u,int fath,int S)
    {
        int pos=0;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            if (size[v]>size[pos]) pos=v;
        }
        if (pos!=0&&size[pos]>=S/2) return Get_Root(pos,u,S);
        return u;
    }
    void Get_Dist(int u,int fath,int D)
    {
        d[++cnt]=D; size[u]=1;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Dist(v,u,D+a[i].w);
            size[u]+=size[v];
        }
    }
    int Get_Ans(int u,int Len)
    {
        d[cnt=0]=0;
        Get_Dist(u,0,Len);
        sort(d+1,d+1+cnt);
        int l=1,r=cnt,an=0;
        while (l<=r) {
            if (d[l]+d[r]<=k) an=an+r-l,l++;
            else r--;
        }
        return an;
    }
    void solve(int u)
    {
        ans=ans+Get_Ans(u,0);
        use[u]=true;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (use[v]) continue;
            ans=ans-Get_Ans(v,a[i].w);
            int rt=Get_Root(v,u,size[v]);
            solve(rt);
        }
    }
    int main()
    {
        read(n);
        int u,v,w,t;
        fp(i,2,n) read(u,v,w),adde(u,v,w),adde(v,u,w);
        read(k);
        solve(1);
        cout<<ans<<'
    ';
        return 0;
    }
    View Code

    但是为什么我学了点分治呢???

    事情是这样的,这天翻译到一道试题,发现是一个关于xor的性质的题目。

    马上就想到异或前缀和+Tire树。然后发现我看错题目了。

    COCI 2018/2019 Problem3 Deblo

    这道题概括起来就是:

    求树上路径点权异或和之和

    事实上这道题如果放在边权上直接就出来了!

    你就可以跑一边dfs,记录xor前缀和,然后问题就转化为:求一个数组d(就是xor前缀和数组),第l项和第r项(r>=l)相减(d[r]-d[l]),求和

    然后你弄个桶从左往右扫就完美O(n log2 n)了。。

    但是但是这个东西是点权,如果你那样做会抠掉LCA!!!

    这样做的复杂度是O(n2 log2 n)?加点常数还不如O(n3)裸暴力?!

    一定有高妙的办法。

    淀粉质 点分治是解决树上路径的利器

    我们考虑到:树上路径的形成无外乎两种情况,在一棵以x为子树根的子树中,u到v的一条路径要么是跨过x,要么是不跨过x的。

    进一步,如果u到v这条路径跨过x,那么 u和v要么同时在x的同一个子树里面,要么在同时在x不同的子树外!(看图)

    我们想到可以在确定一个子树根的情况下,分别遍历他的每一个子树,分别统计答案然后把答案“拼接”起来,形成最终要求的答案。

    当然只分出一个根还不够,每一次深入我们都需要找到一个根,然后来遍历他的各个子树。

    我们现在的模型就抽象成上面那样,一条合法的路径无外乎两种情况:

    1. 灰色里面的一条过root的路径和红色里面一条过root的路径"拼接"起来

    2.整条路径被灰色和红色子树包含

    但是...要分类?

    我们考虑分而治之:再细分会发生什么?

    我们发现刚才的担心是多于,那是因为我只要逐步细分一定都可以作为第一种情况考虑,所以在分治的过程中我们只考虑第一种情况,不必要分类讨论。

    那么怎么选root呢?这个事情就和树链剖分为什么剖长链一样,只是为了递归层数低一些。

     显然选X比选Y优,选X只要递归2层而选Y要递归4层!

    一个结论:我们要选子树的重心

    我们对于重心的定义是这样的:这个点下面的子树元素个数占到整个子树元素个数的1/2还多一点点!

    int Get_Root(int u,int fath,int S)//S是指以u为根的子树的size
    {
        int pos=0;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (v==fath||used[v]) continue;
            if (size[v]>size[pos]) pos=v;
        }
        if (pos!=0&&size[pos]>=S/2) return Get_Root(pos,u,S);
        return u;
    }

    根据重心的定义,我们可以写出这样一个得到以u为根子树的重心的函数。(复杂度O(n))

    找到子树的根了那么就可以算出以当前根root为子树根下面节点的异或前缀和。并把这些异或前缀和按照二进制位的方法存到一个桶cnt中。

    注意到cnt[0][i]表示第i个二进制位是0的前面有多少个数,cnt1[1][i]表示第i个二进制位是1有多少个数。

    由于异或运算不能像上面一样相减,我们考虑在处理完一棵子树以后再把这棵子树的信息加入到桶里面。

    再来看Get_Dist函数,就相当于把以u为根的子树(含u)所涵盖节点异或前缀和加入到桶里面。

    void Get_Dist(int u,int fath,int num)
    {
        num^=val[u];
        fp(i,0,23)
         cnt[(num & (1<<i))>0][i]++;
        size[u]=1;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Dist(v,u,num);
            size[u]+=size[v];
        }
    }

    而Get_Ans函数,就相当于把以u为根的子树(含u)所涵盖节点异或前缀和和原有路径的异或前缀和进行异或,得到横跨节点u的一整条路径。

    当然每次求一条路径必须不能包含这条路径上的节点(所以要先Get_Ans再Get_Dist!)

    void Get_Ans(int u,int fath,int num)
    {
        num^=val[u];
        fp(i,0,23)
         ans+=(LL)(1<<i)*cnt[(num & (1<<i))==0][i];
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Ans(v,u,num);
        }
    }

    至于根的问题由于横跨所有路径都会算上根,事先在solve中就已经把根加入桶了。

    当然 , 我们没有考虑根到根这一条路径所以必须把它补上。

    这就是solve函数了!!!

    void solve(int u)
    {
        use[u]=true;
        memset(cnt,0,sizeof(cnt));
        fp(i,0,23)
         cnt[(val[u] & (1<<i))>0][i]++;
        ans+=(LL)val[u];
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (use[v]) continue;
            Get_Ans(v,0,0);
            Get_Dist(v,0,val[u]);
        }
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (use[v]) continue;
            int rt=Get_Root(v,u,size[v]);
            solve(rt);
        }
    }

    这里有个复杂度的问题:我们在点分治过程中每次选取子树的重心为子树的树根进行处理, 这样总的递归深度不会超过logN层,

    整个点分治的时间复杂度也就保证了O(NlogN)

    代码的话这里放一下吧:

    Code:

    # include <bits/stdc++.h>
    # define fp(i,s,t) for (int i=s;i<=t;i++)
    # define LL long long
    using namespace std;
    const int N=100005;
    int head[N],size[N],d[N],n,tot,k;
    bool use[N];
    struct rec{int pre,to;}a[N<<1];
    int cnt[2][25],val[N];
    LL ans;
    template<typename T>void read(T &x)
    {
        x=0; int w=0; char c=0;
        while (c<'0'||c>'9') w|=c=='-',c=getchar();
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=w?-x:x;
    }
    template <typename T,typename... Args> inline void read(T& t, Args&... args)
    {
        read(t);read(args...);
    }
    void adde(int u,int v)
    {
        a[++tot].pre=head[u];
        a[tot].to=v;
        head[u]=tot;
    }
    int Get_Root(int u,int fath,int S)
    {
        int pos=0;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            if (size[v]>size[pos]) pos=v;
        }
        if (pos!=0&&size[pos]>=S/2) return Get_Root(pos,u,S);
        return u;
    }
    void Get_Dist(int u,int fath,int num)
    {
        num^=val[u];
        fp(i,0,23)
         cnt[(num & (1<<i))>0][i]++;
        size[u]=1;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Dist(v,u,num);
            size[u]+=size[v];
        }
    }
    void Get_Ans(int u,int fath,int num)
    {
        num^=val[u];
        fp(i,0,23)
         ans+=(LL)(1<<i)*cnt[(num & (1<<i))==0][i];
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Ans(v,u,num);
        }
    }
    void solve(int u)
    {
        use[u]=true;
        memset(cnt,0,sizeof(cnt));
        fp(i,0,23)
         cnt[(val[u] & (1<<i))>0][i]++;
        ans+=(LL)val[u];
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (use[v]) continue;
            Get_Ans(v,0,0);
            Get_Dist(v,0,val[u]);
        }
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (use[v]) continue;
            int rt=Get_Root(v,u,size[v]);
            solve(rt);
        }
    }
    int main()
    {
        read(n);
        int u,v;
        fp(i,1,n) read(val[i]);
        fp(i,2,n) read(u,v),adde(u,v),adde(v,u);
        solve(1);
        cout<<ans<<'
    ';
        return 0;
    }
    View Code

    我推荐几个练习(雾) 

    【luogu P2634】 [国家集训队]聪聪可可

    # include <cstdio>
    # define LL long long
    using namespace std;
    const int N=5e4+10;
    struct rec{ int pre,to,w; }a[N<<1];
    int n,tot,ans,SIZE,root;
    int head[N],d[N],size[N],f[N];
    int r[3];
    bool use[N];
    int max(int x,int y){return (x>y)?x:y;}
    int min(int x,int y){return (x>y)?y:x;}
    inline int read()
    {
        int X=0,w=0; char c=0;
        while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
        while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
        return w?-X:X;
    }
    void adde(int u,int v,int w)
    {
        a[++tot].pre=head[u];
        a[tot].to=v;
        a[tot].w=w;
        head[u]=tot;
    }
    void Get_Root(int u,int fath)
    {
        f[u]=0,size[u]=1;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Root(v,u);
            f[u]=max(f[u],size[v]);
            size[u]+=size[v];
        }
        f[u]=max(f[u],SIZE-size[u]);
        if (f[u]<f[root]) root=u;
    }
    int cnt;
    void Get_Dist(int u,int fath,int L)
    {
        r[L%3]++;
        d[++cnt]=L; size[u]=1;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Dist(v,u,L+a[i].w);
            size[u]+=size[v];
        }
    }
    int Get_Ans(int u,int fath,int L)
    {
        cnt=0; int ret=0;
        r[0]=r[1]=r[2]=0;
        Get_Dist(u,fath,L);
        for (int i=1;i<=cnt;i++)
         if (d[i]%3==0) ret+=r[0]-1;
         else if (d[i]%3==1) ret+=r[2];
         else if (d[i]%3==2) ret+=r[1];
        return ret;
    }
    void solve(int u)
    {
        use[u]=true;
        ans+=(LL)Get_Ans(u,0,0);
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to; if (use[v]) continue;
            ans-=(LL)Get_Ans(v,u,a[i].w);
            SIZE=size[v],root=0;
            Get_Root(v,u);
            solve(root);
        }
    }
    LL gcd(LL a,LL b){return (b==0ll)?a:gcd(b,a%b);}
    int main()
    {
        f[0]=1e9; n=read();
        int u,v,w;
        for (int i=1;i<n;i++) {
            u=read();v=read();w=read();
            adde(u,v,w); adde(v,u,w);
        }
        SIZE=n; root=0;
        Get_Root(1,0);
        solve(root); ans+=(LL)n;
        LL g=gcd(ans,n*n);
        LL a=ans/g,b=(LL)n*n/g;
        printf("%lld/%lld",a,b);
        return 0;
    }
    View Code

    【luogu模板】点分治1 询问树上距离为k的点对是否存在

    # pragma G++ optimize(3)
    # include <bits/stdc++.h>
    using namespace std;
    const int N=1e4+10;
    struct rec{int pre,to,w;}a[N<<1];
    int d[N],q[N],head[N],size[N],tot,n,m;
    bool use[N];
    int  bo[10000000];
    void read(int& x)
    {
        x=0;char c=0; bool w=false;
        while (c<'0'||c>'9') w|=c=='-',c=getchar();
        while ('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        if (w) x=-x;
    }
    void adde(int u,int v,int w)
    {
        a[++tot].pre=head[u];
        a[tot].to=v;
        a[tot].w=w;
        head[u]=tot;
    }
    int Get_Root(int u,int fath,int S)
    {
        int pos=0;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            if (size[v]>size[pos]) pos=v;
        }
        if (pos!=0&&size[pos]>=(S>>1)) return Get_Root(pos,u,S);
        return u;
    }
    int cnt;
    void Get_Dist(int u,int fath,int L)
    {
        d[++cnt]=L; size[u]=1;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Dist(v,u,L+a[i].w);
            size[u]+=size[v];
        }
    }
    void Get_Ans(int u,int L,int opx)
    {
        cnt=0;
        Get_Dist(u,0,L);
        for (int i=1;i<=cnt;i++)
         for (int j=1;j<=cnt;j++)
          if (i!=j) bo[d[i]+d[j]]+=opx;
    }
    void solve(int u)
    {
        Get_Ans(u,0,1);
        use[u]=true;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (use[v]) continue;
            Get_Ans(v,a[i].w,-1);
            int rt=Get_Root(v,0,size[v]);
            solve(rt);
        }
    }
    int main()
    {
        read(n);read(m);
        int u,v,w;
        for (int i=1;i<n;i++)
         read(u),read(v),read(w),
         adde(u,v,w),adde(v,u,w);
        solve(1);
        int t;
        for (int i=1;i<=m;i++) {
            read(t);
            puts(bo[t]?"AYE":"NAY");
        }
        return 0;
    }
    View Code

    【luogu P4178】 Tree 求小于k的点对个数

    # include <bits/stdc++.h>
    # define fp(i,s,t) for (int i=s;i<=t;i++)
    using namespace std;
    const int N=40005;
    int head[N],size[N],d[N],cnt,n,tot,ans,k;
    bool use[N];
    struct rec{int pre,to,w;}a[N<<1];
    template<typename T>void read(T &x)
    {
        x=0; int w=0; char c=0;
        while (c<'0'||c>'9') w|=c=='-',c=getchar();
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=w?-x:x;
    }
    template <typename T,typename... Args> inline void read(T& t, Args&... args)
    {
        read(t);read(args...);
    }
    void adde(int u,int v,int w)
    {
        a[++tot].pre=head[u];
        a[tot].to=v;
        a[tot].w=w;
        head[u]=tot;
    }
    int Get_Root(int u,int fath,int S)
    {
        int pos=0;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            if (size[v]>size[pos]) pos=v;
        }
        if (pos!=0&&size[pos]>=S/2) return Get_Root(pos,u,S);
        return u;
    }
    void Get_Dist(int u,int fath,int D)
    {
        d[++cnt]=D; size[u]=1;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Dist(v,u,D+a[i].w);
            size[u]+=size[v];
        }
    }
    int Get_Ans(int u,int Len)
    {
        d[cnt=0]=0;
        Get_Dist(u,0,Len);
        sort(d+1,d+1+cnt);
        int l=1,r=cnt,an=0;
        while (l<=r) {
            if (d[l]+d[r]<=k) an=an+r-l,l++;
            else r--;
        }
        return an;
    }
    void solve(int u)
    {
        ans=ans+Get_Ans(u,0);
        use[u]=true;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (use[v]) continue;
            ans=ans-Get_Ans(v,a[i].w);
            int rt=Get_Root(v,u,size[v]);
            solve(rt);
        }
    }
    int main()
    {
        read(n);
        int u,v,w,t;
        fp(i,2,n) read(u,v,w),adde(u,v,w),adde(v,u,w);
        read(k);
        solve(1);
        cout<<ans<<'
    ';
        return 0;
    }
    View Code

    【CF161D】 :求距离为k的点对个数

    # pragma G++ optimize(3)
    # include <bits/stdc++.h>
    # define LL long long
    using namespace std;
    const int N=5e4+10;
    struct rec{ int pre,to; }a[N<<1];
    int n,k,tot;
    LL ans;
    int d[N],size[N],head[N];
    bool use[N];
    void read(int& x)
    {
        x=0;char c=0; bool w=false;
        while (c<'0'||c>'9') w|=c=='-',c=getchar();
        while ('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        if (w) x=-x;
    }
    void adde(int u,int v)
    {
        a[++tot].pre=head[u];
        a[tot].to=v;
        head[u]=tot;
    }
    int Get_Root(int u,int fath,int S)
    {
        int pos=0;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            if (size[v]>size[pos]) pos=v;
        }
        if (pos!=0&&size[pos]>=(S>>1)) return Get_Root(pos,u,S);
        return u;
    }
    int cnt;
    void Get_Dist(int u,int fath,int L)
    {
        d[++cnt]=L; size[u]=1;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Dist(v,u,L+1);
            size[u]+=size[v];
        }
    }
    void Get_Ans(int u,int L,int opx)
    {
        cnt=0; Get_Dist(u,0,L);
        sort(d+1,d+1+cnt);
        for (int i=1;i<=cnt;i++) {
         int *p1=lower_bound(d+1,d+1+cnt,k-d[i]);
         int t1=p1-d;
         int *p2=upper_bound(d+1,d+1+cnt,k-d[i]);
         int t2=p2-d-1;
         if (t2<t1) continue;
         if (t1==0||t2==0) continue;
         if ((d[t1]!=k-d[i])||(d[t2]!=k-d[i])) continue;
         ans+=(LL)(opx)*(t2-t1+1);
         if (i>=t1&&i<=t2) ans-=opx;
        }
    }
    void solve(int u)
    {
        use[u]=true;
        Get_Ans(u,0,1);
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (use[v]) continue;
            Get_Ans(v,1,-1);
            int rt=Get_Root(v,0,size[v]);
            solve(rt);
        }
    }
    signed main()
    {
         read(n);read(k);
        int u,v;
        for (int i=1;i<n;i++)
            read(u),read(v),
            adde(u,v),adde(v,u);
        solve(1);
        cout<<(ans>>1)<<'
    ';
        return 0;
    }
    View Code

    [IOI2011]Race :权值和为k,求最小边数

    # include <cstdio>
    # define inf (0x3f3f3f3f)
    using namespace std;
    const int N=2e5+10;
    const int K=1e6+10;
    struct rec { int pre,to,w; }a[N<<1];
    int n,k,tot,ans=inf,root,SIZE;
    int head[N],size[N],tmp[K],f[N];
    bool use[N];
    inline int min(int x,int y){ return x>y?y:x;}
    inline int max(int x,int y){ return x>y?x:y;}
    inline int read(int &X)
    {
        X=0; bool w=0; char c=0;
        while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
        while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
        return w?-X:X;
    }
    void adde(int u,int v,int w)
    {
        a[++tot].pre=head[u];
        a[tot].to=v;
        a[tot].w=w;
        head[u]=tot;
    }
    void Get_Root(int u,int fath)
    {
        f[u]=0,size[u]=1;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Root(v,u);
            f[u]=max(f[u],size[v]);
            size[u]+=size[v];
        }
        f[u]=max(f[u],SIZE-size[u]);
        if (f[u]<f[root]) root=u;
    }
    void Get_Dist(int u,int fath,int num,int L)
    {
        if (L>k) return;
        tmp[L]=min(tmp[L],num);
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Dist(v,u,num+1,L+a[i].w);
        }
    }
    void Get_Ans(int u,int fath,int num,int L)
    {
        if (L>k) return;
        ans=min(ans,tmp[k-L]+num);
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            Get_Ans(v,u,num+1,L+a[i].w);
        }
    }
    void clear(int u,int fath,int L)
    {
        if (L>=k) return;
        tmp[L]=inf;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to;
            if (v==fath||use[v]) continue;
            clear(v,u,L+a[i].w);
        }
    }
    void solve(int u)
    {
        use[u]=true; tmp[0]=0;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (use[v]) continue;
            Get_Ans(v,u,1,a[i].w);
            Get_Dist(v,u,1,a[i].w);
        }
        clear(u,0,0);
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to;
            if (use[v]) continue;
            SIZE=size[v],root=0;
            Get_Root(v,u);
            solve(root);
        }
    }
    int main()
    {
        read(n);read(k); f[0]=inf;
        for (int i=1;i<n;i++) {
            int u,v,w;
            read(u);read(v);read(w);
            adde(u+1,v+1,w); adde(v+1,u+1,w);
        }
        for (int i=0;i<=k;i++) tmp[i]=inf;
        SIZE=n,root=0; Get_Root(1,0);
        solve(root);
        if (ans==inf) puts("-1");
        else printf("%d
    ",ans);
        return 0;
    }
    View Code
  • 相关阅读:
    JavaScript面向对象之类的创建
    VSCode Debug模式下各图标 含义
    Visual Studio icon 含义
    Unity坐标系 左手坐标系 图
    Unity类继承关系 图
    Unity的SendMessage方法
    ONGUI->NGUI->UGUI (Unity UI史)
    Console.WriteLine 不会输出到unity控制台
    Chrome添加Unity本地文档引擎
    Unity "Build failed : Asset is marked as don't save " 解决方案
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/10347198.html
Copyright © 2020-2023  润新知