zoukankan      html  css  js  c++  java
  • POJ1741 经典树分治

    题意:有一棵树,每条边有一个距离,求dis(u,v)<=k的点的对数

    题解:树分治,对于一颗树上的两点,要么在同一颗子树上,要么在不同子树上,要么一个点是根,另一个在某一子树上,对于第一种情况我们可以通过递归来变成第二种或者第三种情况。我们对于某一颗子树来说我们先统计dis[u]+dis[v]<=k的点的对数,然后把该子树的所有子节点为根的这颗子树中dis[u]+dis[v]<=k的点的对数删去,因为在递归到后面会计算重复(这是第一种情况),所以这样就得到了该子树的满足条件的点对数,那么剩下就是如何分了,要想使分的次数最小,那么我们可以每次选取该子树的重心来作为根节点,然后,每次计算完之后删除根节点,再计算所有子树,计算重心还是通过树形dp来求解,

    对于树分治来说,分为点分治和边分治,点分治就是每次找到重心,然后把重心删除掉,对分成的树之间统计路径信息,直到只剩一个点的情况

    #include<map>
    #include<set>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<cstdio>
    #include<cassert>
    #include<iomanip>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    #define pii pair<int,int>
    #define C 0.5772156649
    #define pi acos(-1.0)
    #define ll long long
    #define mod 1000000007
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    
    using namespace std;
    using namespace __gnu_cxx;
    
    const double g=10.0,eps=1e-7;
    const int N=10000+10,maxn=300000+10,inf=0x3f3f3f;
    
    struct edge{
        int to,Next,c;
    }e[maxn];
    int cnt,head[N];
    int sz[N],zx[N],dis[N];
    int n,k;
    bool vis[N];
    void add(int u,int v,int c)
    {
        e[cnt].to=v;
        e[cnt].c=c;
        e[cnt].Next=head[u];
        head[u]=cnt++;
    }
    void init()
    {
        cnt=0;
        memset(head,-1,sizeof head);
        memset(vis,0,sizeof vis);
    }
    void dfssz(int u,int f,int &sum)
    {
        sum++;
        sz[u]=1;
        for(int i=head[u];~i;i=e[i].Next)
        {
            int x=e[i].to;
            if(x==f||vis[x])continue;
            dfssz(x,u,sum);
            sz[u]+=sz[x];
        }
    }
    void dfszx(int u,int f,int sum,int &ans,int &id)
    {
        zx[u]=sum-sz[u];
        for(int i=head[u];~i;i=e[i].Next)
        {
            int x=e[i].to;
            if(x==f||vis[x])continue;
            dfszx(x,u,sum,ans,id);
            zx[u]=max(zx[u],sz[x]);
        }
        if(ans>zx[u])
        {
            ans=zx[u];
            id=u;
        }
    }
    int findzx(int root)//找子树的重心
    {
        int sum=0;
        dfssz(root,-1,sum);
        int ans=inf,id=0;
        dfszx(root,-1,sum,ans,id);
        return id;
    }
    void dfsdis(int u,int f,vector<int> &son)
    {
        for(int i=head[u];~i;i=e[i].Next)
        {
            int x=e[i].to;
            if(x==f||vis[x])continue;
            dis[x]=dis[u]+e[i].c;
            dfsdis(x,u,son);
        }
        son.pb(dis[u]);
    }
    bool comp(int a,int b)
    {
        return dis[a]<dis[b];
    }
    int ok(vector<int> &son)
    {
        sort(son.begin(),son.end());
       // for(int i=0;i<son.size();i++)
       //     cout<<son[i]<<endl;
       // cout<<endl;
        int ans=0;
        for(int i=0;i<son.size();i++)
        {
            int p=upper_bound(son.begin(),son.end(),k-son[i])-son.begin();
            if(p>0)p--;
            if(p<=i)continue;
          //  cout<<p<<endl;
            ans+=p-i;
        }
       // cout<<ans<<"*****"<<endl;
        return ans;
    }
    void dfsson(int u,int f,vector<int> &son)
    {
        son.pb(dis[u]);
        for(int i=head[u];~i;i=e[i].Next)
        {
            int x=e[i].to;
            if(x==f||vis[x])continue;
            dfsson(x,u,son);
        }
    }
    int solve(int root)
    {
        int p=findzx(root);//找该子树的重心
        memset(dis,-1,sizeof dis);
        dis[p]=0;
        vector<int>son;
        son.clear();
        dfsdis(p,-1,son);//求子树中各点到重心的距离
        int ans=ok(son);//满足该子树的dis[x]+dis[y]<=k对数;
      //  if(root==3)cout<<ans<<" ----"<<endl;
        for(int i=head[p];~i;i=e[i].Next)
        {
            int x=e[i].to;
            if(vis[x])continue;
            son.clear();
            dfsson(x,p,son);
            ans-=ok(son);
        }
      //  if(root==3)cout<<ans<<" ****"<<endl;
        vis[p]=1;
        for(int i=head[p];~i;i=e[i].Next)
        {
            int x=e[i].to;
            if(vis[x])continue;
            ans+=solve(e[i].to);
            //if(x==3)cout<<solve(e[i].to)<<"......."<<endl;
        }
      //  if(root==3)cout<<p<<" "<<ans<<"+++++++++"<<endl;
        return ans;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        while(~scanf("%d%d",&n,&k))
        {
            if(!n&&!k)break;
            init();
            for(int i=1;i<n;i++)
            {
                int a,b,c;
                scanf("%d%d%d",&a,&b,&c);
                add(a,b,c);
                add(b,a,c);
            }
            printf("%d
    ",solve(1));
        }
        return 0;
    }
    /*******************
    4 3
    1 2 3
    1 3 1
    1 4 2
    ********************/
    View Code
  • 相关阅读:
    54:代码审计-TP5框架审计写法分析及代码追踪
    53:代码审计-TP5框架及无框架变量覆盖反序列化
    52:代码审计-PHP项目类RCE及文件包含下载删除
    51:代码审计-PHP框架MVC类上传断点调试挖掘
    支配树学习笔记
    模拟费用流学习笔记
    python之元类、双下方法( 双下方法也叫魔术方法、 内置方法)
    java 注解
    java 反射
    java synchronized
  • 原文地址:https://www.cnblogs.com/acjiumeng/p/7815123.html
Copyright © 2011-2022 走看看