• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
AC_Artist.zig_zag
然而我依然在补题、
博客园    首页    新随笔    联系   管理    订阅  订阅

Poj 1741 Tree

据说是传说中的男人八题之一,其实是点分治的练手题......不过第一次写调起来还是比较恶心的。

话说QZC的论文我是完全没有看懂,上网找了一份代码看看才有点思路。

树分治算法大致要分成几个过程:

预处理->分治{分割,计算答案,递归处理子树}就是这样了。

但实际写起来却要写各种dfs和bfs,但这种东西点双边双写多了就不会出什么错了,我在find计算答案那块调了好久,后来发现dis的下标应该是队列的标记,而不是某个点。

我写的点分治,主过程是solve,递归地solve下去:每次先choose一个重心,然后计算这颗树里所有答案(就是计算出每个点到根的距离然后判断即可),然后减去路径不经过根的答案(就是在儿子里find),然后把根断掉,以后就再也不会用到这个根了,然后递归地处理根的所有子树就行了。

Tree
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<cstring>
  6 #define inf 2147483647
  7 #define maxn 20000
  8 using namespace std;
  9 
 10 struct et
 11 {
 12     int s,t,val,next;
 13 }e[maxn];
 14 int fir[maxn],fa[maxn],dis[maxn],size[maxn],q[maxn];
 15 bool ex[maxn];
 16 int n,m,tot,num;
 17 long long ans;
 18 
 19 void dfs(int now)//dfs初始化树的信息
 20 {
 21     size[now]=1;
 22     for (int j=fir[now];j;j=e[j].next)
 23     {
 24         int k=e[j].t;
 25         if (k!=fa[now])
 26         {
 27             fa[k]=now;
 28             dfs(k);
 29             size[now]+=size[k];
 30         }
 31     }
 32 }
 33 
 34 void choose(int &rot)//找到当前子树的重心并以之为根
 35 {
 36     int now,mx=0;
 37     for (int j=fir[rot];j;j=e[j].next)
 38     {
 39         int k=e[j].t;
 40         if (ex[k]&&size[k]>mx&&k!=rot)//ex防止走出当前子树,下同
 41         {
 42             mx=size[k];//记录rot的儿子里size最大的
 43             now=k;
 44         }
 45     }
 46     if (mx>num/2)//找到重心,而且它的size一定比rot大,于是修改根
 47     {
 48         size[rot]-=size[now];
 49         fa[rot]=now; fa[now]=0;
 50         size[now]=num;
 51         rot=now;
 52         choose(rot);
 53     }
 54 }
 55 
 56 int find(int rot,int dist)//计算以rot为根的答案
 57 {
 58     int head=0,tail=1;
 59     q[1]=rot; dis[1]=dist;
 60     while (head<tail)//广搜计算距离
 61     {
 62         int now=q[++head];
 63         for (int j=fir[now];j;j=e[j].next)
 64         {
 65             int k=e[j].t;
 66             if (ex[k]&&fa[now]!=k)//判断不是父向边
 67             {
 68                 q[++tail]=k;
 69                 dis[tail]=dis[head]+e[j].val;    
 70             }
 71         }
 72     } 
 73     sort(dis+1,dis+tail+1);//log(n)排序
 74     int sum=0;
 75     int j=tail;
 76     for (int i=1;i<=tail;i++)//O(n)累加答案
 77     {
 78         while (dis[i]+dis[j]>m&&j>i) j--;
 79         if (j>i) sum+=(j-i);
 80     }
 81     return sum;
 82 }
 83 
 84 void solve(int rot)
 85 {
 86     if (num==1) return;
 87     choose(rot);
 88     ans+=find(rot,0);
 89     for (int j=fir[rot];j;j=e[j].next)//减去所有不过根的答案
 90     {
 91         int k=e[j].t;
 92         if (ex[k]) ans-=find(k,e[j].val);
 93     }
 94     ex[rot]=0;//切掉rot,把树分成若干子树
 95     for (int j=fir[rot];j;j=e[j].next)//分治子树
 96     {
 97         int k=e[j].t;
 98         if (ex[k]) 
 99         {
100             num=size[k];//num在这层已经没用了,所以可以开全局
101             solve(k);
102         }
103     }
104 }    
105 
106 void add(int x,int y,int z)
107 {
108     e[++tot].s=x; e[tot].t=y; e[tot].val=z; e[tot].next=fir[x]; fir[x]=tot;
109     e[++tot].s=y; e[tot].t=x; e[tot].val=z; e[tot].next=fir[y]; fir[y]=tot;
110 }
111 
112 int main()
113 {
114     scanf("%d%d",&n,&m);
115     while (n+m)
116     {
117         ans=0;tot=0;
118         memset(ex,1,sizeof(ex));
119         memset(fir,0,sizeof(fir));
120         memset(dis,0,sizeof(dis));
121         int x,y,z;
122         for (int i=1;i<n;i++)
123         {
124             scanf("%d%d%d",&x,&y,&z);
125             add(x,y,z);    
126         }
127         int rot=1;
128         dfs(rot);
129         num=n;
130         solve(rot);
131         printf("%I64d\n",ans);
132         scanf("%d%d",&n,&m);
133     }
134 }

最近突然发现dev的单步还是挺好用的,以后不用guide这个恶心的东西了。

AC without art, no better than WA !
posted @ 2013-04-01 18:25  Zig_zag  阅读(315)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3