• 【学习笔记】点分治


    点分治

    模板

    下文若未特殊指明,路径均指不重复经过某一条边的路径。

    点分治,顾名思义,是在树上的分治。

    设想我们要找是否存在长度为(k)的路径,该怎么办呢?

    首先,我们将路径分类,分别为经过点(root)的路径,和在点(root)子树下的路径。

    显然,对于第二种情况可以转化为第一种情况。

    对于第一种情况,我们把每个点到(root​)的距离预处理出来,再用二分等方法求出方案数。就可以完美通过。

    等等,真的完美吗?

    我们发现,这里面经过(root)的路径可能经过重复的边。这是不合法的。不过,这种情况只会出在同一个子树下。

    所以,在统计路径时,当处理到(root)的某个子树时,要先减掉这种路径。这样才能保证所有计算的路径合法。这样的程序才正确的。

    然而,这样做其实会被出题人卡,当发生链的情况时,时间复杂度就会退化。

    该如何解决此问题?

    其实,我们可以证明,当选择树的重心时,时间复杂度最优。时间复杂度(O(nlog^2n)​)

    #include<bits/stdc++.h>
    using namespace std;
    int cc,to[30000],net[30000],fr[30000],len[30000];
    int q[30000],jl[30000],siz[30000],fa[30000];bool vis[30000]={0};
    int maxr,n,m,root,h,t,k,ans,a,b,c;
    void addedge(int u,int v,int c)
    {
        cc++;
        to[cc]=v;net[cc]=fr[u];fr[u]=cc;len[cc]=c;
    }
    bool cmp(int x,int y)
    {
        return jl[x]<jl[y];
    }
    void findroot(int x)
    {
        int maxx=0;
        siz[x]=1;
        for (int i=fr[x];i;i=net[i])
        {
            if (to[i]==fa[x]||vis[to[i]]) continue;
            fa[to[i]]=x;findroot(to[i]);
            siz[x]+=siz[to[i]];
            maxx=max(maxx,siz[to[i]]);
        }
        maxx=max(maxx,n-siz[x]);
        if (maxx<maxr) maxr=maxx,root=x;
    }
    void dfs(int x)
    {
        q[++t]=x;
        for (int i=fr[x];i;i=net[i])
        {
            if (to[i]==fa[x]||vis[to[i]]) continue;
            jl[to[i]]=jl[x]+len[i];
            fa[to[i]]=x;dfs(to[i]);
        }
    }
    int calc(int l,int r,int z)
    {
        int sum=0;
        sort(q+l,q+r+1,cmp);
        for (int i=l,j=r + 1 ;i<=r;i++)
        {
            if (jl[q[i]]==k&&z){sum++;continue;}
            while ((jl[q[i]]+jl[q[j]]>k&&i<j) ||(j==r+1)) j--;
            if (jl[q[i]]+jl[q[j]]!=k) continue;
            int kk=j;
            while (jl[q[kk]]==jl[q[j]]&&i<kk) kk--;
            sum+=j - kk ; 
        }
        return sum;
    }
    void work(int x)
    {
        maxr=root=2147483647;
        findroot(x);
        x=root;
        h=1;t=0;
        for (int i=fr[x];i;i=net[i])
        {
            jl[to[i]]=len[i];fa[to[i]]=x;
            if (vis[to[i]]) continue;
            dfs(to[i]);
            ans-=calc(h,t,0);
            h=t+1;
        }
        ans+=calc(1,t,1);
        vis[x]=true;
        for (int i=fr[x];i;i=net[i])
        {
            if (vis[to[i]]) continue;
            work(to[i]);
        }
    }
    int main()
    {
        cin>>n>>m;
        for (int i=1;i<n;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            addedge(a,b,c);
            addedge(b,a,c);
        }
        for (int i=1;i<=m;i++)
        {
            scanf("%d",&k);ans=0;
            for (int j=1;j<=n;j++)
              vis[j]=false,fa[j]=0,jl[j]=0;
            work(1);
            if (ans>0) cout<<"AYE
    ";else cout<<"NAY
    ";
        }
        return 0;
    }
    

    课后练习

    CF161D

    聪聪可可

  • 相关阅读:
    临时
    vue数据立刻绑定到dom元素
    java浅拷贝和深拷贝(基础也是很重要的)
    Eclipse快捷键大全
    微信小程序开发之路之组件化
    c++入门笔记
    开发个活动上线是个什么体验
    分布式锁的理解,java自带的锁为什么会失效
    vue页面开发遇到的坑,都是泪!src属性,freemarker取值
    java泛型理解。代码更明了。
  • 原文地址:https://www.cnblogs.com/fmj123/p/10450029.html
Copyright © 2020-2023  润新知