• POJ 1741 Tree(点分治)


    http://poj.org/problem?id=1741

    题意:

    给出一棵树,求出树上满足两点权值之和不大于k的点对数。

    思路:

    直接暴力就是$O(n^2)$,显然不行。这里有一篇论文可以推荐大家看一下

    因为一条路径要么过根结点,要么不过,即在一棵子树中,然后我们可以用分治法。每次确定一个根u,u有几棵子树,就将u的子树分成了几部分,每一部分就一直递归下去。

    我们设dis[i]为i到根结点的距离,于是我们可以得到以下的算法:$dis[i]+dis[j]<=k的(i,j)对数-dis[i]+dis[j]<=k的(i,j)对数且(i,j)在一棵子树中$。这个部分其实不难算,我们只需要先把所有子节点的dis值求出,然后排个序,利用尺取法可以快速求出个数。

    还有一点要注意的是,根结点的选择很重要,这里需要选择重心,因为删去重心后最大子树的节点数最小。

      1 #include<iostream>
      2 #include<algorithm>
      3 #include<cstring>
      4 #include<cstdio>
      5 #include<vector>
      6 #include<stack>
      7 #include<queue>
      8 #include<cmath>
      9 #include<map>
     10 #include<set>
     11 using namespace std;
     12 typedef long long ll;
     13 typedef pair<int,int> pll;
     14 const int INF = 0x3f3f3f3f;
     15 const int maxn=10000+5;
     16 
     17 int n,k,num,root,sz,ans,mi;
     18 int vis[maxn], son[maxn], dis[maxn], mx[maxn];
     19 vector<pll> G[maxn];
     20 
     21 
     22 void getroot(int u, int fa, int n) //计算重心
     23 {
     24     son[u]=0;
     25     int balance=0;
     26     for(int i=0;i<G[u].size();i++)
     27     {
     28         int v=G[u][i].first;
     29         if(v==fa || vis[v])  continue;
     30         getroot(v,u,n);
     31         son[u]+=son[v]+1;
     32         balance=max(balance,son[v]+1);
     33     }
     34     balance=max(balance,n-son[u]-1);
     35     if(balance<sz)
     36     {
     37         sz=balance;
     38         root=u;
     39     }
     40 }
     41 
     42 
     43 void getdis(int u, int fa, int d)  //求子节点到根结点的距离
     44 {
     45     dis[num++]=d;
     46     for(int i=0;i<G[u].size();i++)
     47     {
     48         int v=G[u][i].first;
     49         int w=G[u][i].second;
     50         if(v==fa || vis[v])  continue;
     51         getdis(v,u,d+w);
     52     }
     53 }
     54 
     55 int getnum()
     56 {
     57     int ret=0;
     58     sort(dis,dis+num);
     59     int l=0,r=num-1; 
     60     while(l<r)  //尺取法快速求对数
     61     {
     62         while(dis[l]+dis[r]>k && l<r)  r--;
     63         ret+=r-l;
     64         l++;
     65     }
     66     return ret;
     67 }
     68 
     69 void solve(int u, int n)
     70 {
     71     sz=INF;
     72     getroot(u,-1,n);
     73     num=0;
     74     getdis(root,-1,0);
     75     ans+=getnum();
     76     vis[root]=1;
     77     int tmp=root;
     78     for(int i=0;i<G[tmp].size();i++)
     79     {
     80         int v=G[tmp][i].first;
     81         int w=G[tmp][i].second;
     82         if(!vis[v])
     83         {
     84             num=0;
     85             getdis(v,-1,w);  //减去在同一棵子树的情况
     86             ans-=getnum();
     87             solve(v,son[v]+1);
     88         }
     89     }
     90 }
     91 
     92 int main()
     93 {
     94     //freopen("in.txt","r",stdin);
     95     while(~scanf("%d%d",&n,&k))
     96     {
     97         if(!n && !k)  break;
     98         memset(vis,0,sizeof(vis));
     99         for(int i=1;i<=n;i++)  G[i].clear();
    100         for(int i=1;i<n;i++)
    101         {
    102             int u,v,w;
    103             scanf("%d%d%d",&u,&v,&w);
    104             G[u].push_back(make_pair(v,w));
    105             G[v].push_back(make_pair(u,w));
    106         }
    107         ans=0;
    108         solve(1,n);
    109         printf("%d
    ",ans);
    110     }
    111     return 0;
    112 }
  • 相关阅读:
    [剑指Offer]59-队列的最大值(题目二待补)
    [Java学习]面向对象-package;内部类;UML图表示六种关系
    已安全化的ActiveX控件卸载时出现"DllUnregisterServer函数出错,错误代码:0x80070002"问题解决
    Win10 启动64位IE浏览器——修改注册表方法
    系统托盘弹出通知或气泡
    LPCTSTR 字符串获取其长度
    ActiveX控件获取不到对象属性或者方法的原因分析
    时钟周期 VS 机器周期
    VS2017 ATL创建ActiveX编程要点
    incremental linking(增量链接)的作用
  • 原文地址:https://www.cnblogs.com/zyb993963526/p/7445572.html
Copyright © 2020-2023  润新知