【23.91%】【hdu 4694】Important Sisters("支NMLGB配树"后记)(支配树代码详解)
Time Limit: 7000/7000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 598 Accepted Submission(s): 143
Problem Description
There are N clones of Misaka Mikoto (sisters) forming the Misaka network. Some pairs of sisters are connected so that one of them can pass message to the other one. The sister with serial number N is the source of all messages. All the other sisters get message directly or indirectly from her. There might be more than one path from sister #N to sister #I, but some sisters do appear in all of these paths. These sisters are called important sister of sister #K. What are the important sisters of each sister?
Input
There are multiple test cases. Process to the End of File.
The first line of each test case contains two integers: the number of sisters 1 ≤ N ≤ 50,000 and the number of connections 0 ≤ M ≤ 100,000. The following M lines are M connections 1 ≤ Ai, Bi ≤ N, indicating that Ai can pass message to Bi.
Output
For each test case, output the sum of the serial numbers of important sisters of each sister, separated with single space.
Sample Input
3 2
3 2
2 1
5 7
3 2
1 2
2 1
3 1
3 2
5 3
5 4
Sample Output
6 5 3
9 10 8 9 5
Author
Zejun Wu (watashi)
Source
2013 Multi-University Training Contest 9
【题解】
这题要求支配树的根节点到每个节点的累加和。
啃了好久的支配树。终于啃完了。真是感动啊TAT…
这里非常感谢这个PPT的作者
http://wenku.baidu.com/link?url=Y2tyZ76tS8811FkqjTHVT6lAwJOfdp0CcK00otWMFJzL25fHNGUve8CezjKO9n_AwzhEwBPQXiAYPGSFNF5-f06laP-lXDr55wLiyfB5Sia
以及一大堆博客的博主0 0,基本上百度搜hdu 4694,能搜到的博客都去逛过了。。
首先大家可以看这篇文章;
http://blog.csdn.net/a710128/article/details/49913553
网上找到的关于支配树的文章也就这篇了。
看了个大概之后再来看我下面写的内容;
我用上面这张图来阐述一下啥是半必经点;
节点旁边的数字是时间戳
x的半必经点是u,因为u可以通过时间戳为6,7,8的节点到达x。而且这些时间戳都大于x的时间戳。
也即下面这张图的第二种情况
当然从时间戳为6的节点也可以到达X节点。因此6也是x的半必经点。
但是我们只要时间戳最小的。那么就是u节点了。(在取的时候是取dfn[semi[t]]最小的节点semi[t],且时间戳t>u);
至于图片中的第一种情况。
你可以理解为和第二种情况取min;
即看看x节点的直系父亲(时间戳小于x的)的时间戳是不是比第二种得到的半必经点更小;
我们在做这个算法的时候。求出每个节点的时间戳。
然后从时间戳大的节点开始处理。
这样可以保证之前处理过的节点时间戳都比当前处理的大。
(用并查集)
这样我们可以轻易的维护半必经点了。
以上方法可以求得任意一个节点的半必经点
最后再用这个必经点定理来更新每个节点必经点。
我的代码注释写得比较详细。
看看下面代码再结合资料看一下是可以弄懂的。加油吧~
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int N = 59999;
vector <int> a[N], b[N], pre[N];
int dfn[N], cnt, idom[N], semi[N],f[N],best[N],n,m,id[N],fa[N];
long long ans[N];
void input(int &r)//手动读入
{
char t;
t = getchar();
while (!isdigit(t)) t = getchar();
r = 0;
while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
}
void init()//初始化
{
for (int i = 1; i <= n; i++)
f[i] = best[i] = i;
for (int i = 1; i <= n; i++)
a[i].clear(), b[i].clear(), pre[i].clear(),id[i] = 0;
for (int i = 1; i <= n; i++)
dfn[i] = semi[i] = idom[i] = 0,fa[i] = 0,ans[i] = 0;
cnt = 0;
}
void Dfs(int x)
{
dfn[x] = ++cnt;
id[cnt] = x;//id[x]记录的是时间戳为x的节点是谁。
int len = a[x].size();
for (int i = 0; i <= len - 1; i++)
{
int y = a[x][i];
if (!dfn[y])
Dfs(y), fa[y] = x;
}
}
bool cmp(int x, int y)
{
return dfn[semi[x]] < dfn[semi[y]];
}
int ff(int x)
{
if (f[x] == x)
return x;
int olfa = f[x];
f[x] = ff(f[x]);
best[x] = min(best[x], best[olfa], cmp);
return f[x];
}
void Lengauer_Tarjan()
{
Dfs(n);//dfs获取时间戳
dfn[0] = 0x3f3f3f3f;//这是一个很大的数,一开始的semi都是0,就让他的时间戳无穷大,就不会更新了
for (int i = cnt; i >= 1; i--)//时间戳从大到小处理
{
int x = id[i];
if (i != 1)
{
int len = b[x].size();//b是逆向边
for (int j = 0; j <= len - 1; j++)
{
int y = b[x][j];
if (!dfn[y]) continue;
if (dfn[y] > dfn[x])//如果是dfs序大于的x节点的情况
{//这个并查集里面包括的是时间戳大于某个节点且能够到达这个节点的点
ff(y);//找到所有大于这个节点x的dfs序的节点(一定要是能够到达x节点)
if (dfn[semi[best[y]]] < dfn[semi[x]])//如果时间戳最小的点的半必经点更优
semi[x] = semi[best[y]];//替换这个点的半必经点
}
else
if (dfn[y] < dfn[semi[x]])//这是它的直系父亲(dfn[p]<dfn[x])
semi[x] = y;//如果它的时间戳比之前更新的都小。那么它变成半必经点
}
pre[semi[x]].push_back(x);//记录那个半必经点是相对于谁的半必经点
}
while (pre[x].size())//这个相当于处理出x和semi[x]之间的那个y了。
{
int y = pre[x].back();
pre[x].pop_back();
ff(y);
//值得注意的是。因为我们是时间戳从大到小的顺序处理的。
//所以semi[x]->x路径上的所有点必然已经加入到并查集中。
//且semi[x]上面的点肯定没有包括在并查集中
//因为semi[x]上面的点的时间戳肯定是小于semi[x]的;
//而那些点我们等一下才要处理。
if (semi[best[y]] != x)//如果在semi[y]->y之间存在一个点z
//它的半必经点不等于y的半必经点 则说明要让idom[y] = idom[z];
//但是idom[z]可能还没有处理出来。
//所以我们就仅仅记录这个点z是什么.之后再处理
idom[y] = best[y];
else//如果等于的话,idom[y] = semi[y];
idom[y] = x;
}
int len = a[x].size();
for (int j = 0; j <= len - 1; j++)//然后把这个点加入到并查集维护的森林中
{//当然。只有那些和他有关系的点(能够从这个点到达的点才要加入);
int y = a[x][j];
if (fa[y] == x)
f[y] = x;
}
}
for (int i = 2; i <= cnt; i++)
{
int x = id[i];
if (idom[x] != semi[x])//这个利用上面处理出来的semi[x]->x路径上的点
//利用必经点定理修改必经点
idom[x] = idom[idom[x]];
}
idom[id[1]] = 0;
}
long long get_ans(int x)//这个就是累加答案。
{
if (x == n)
{
ans[x] = n;
return ans[x];
}
if (ans[x])//用了记搜~~
return ans[x];
ans[x] = get_ans(idom[x]) + x;
return ans[x];
}
int main()
{
//freopen("F:\\rush.txt", "r", stdin);
while (~scanf("%d%d", &n, &m))
{
init();
for (int i = 1; i <= m; i++)
{
int x, y;
input(x); input(y);
a[x].push_back(y);
b[y].push_back(x);//反向边用于找其直系爸爸和大于这个时间戳的和它有关的节点。
}
Lengauer_Tarjan();
for (int i = 1; i <= n; i++)
printf("%I64d%c", dfn[i] ? get_ans(i) : 0, i == n ? '\n' : ' ');
}
return 0;
}
【23.91%】【hdu 4694】Important Sisters("支NMLGB配树"后记)(支配树代码详解)的更多相关文章
- HDU.4694.Important Sisters(支配树)
HDU \(Description\) 给定一张简单有向图,起点为\(n\).对每个点求其支配点的编号和. \(n\leq 50000\). \(Solution\) 支配树. 还是有点小懵逼. 不管 ...
- [HDU]4694 Important Sisters(支配树)
支配树模板 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; ...
- hdu 4694 Important Sisters【支配树】
求出支配树输出到father的和即可 支配树见:https://blog.csdn.net/a710128/article/details/49913553 #include<iostream& ...
- 【心情】"支NMLGB配树”
大视野oj坏了 那就做杭电呗 看看大触都做杭电里的哪些题 看到杭电的分类了 Tarjan算法诶,我好像会嘛,就是你了 诶,怎么不是求强连通分量? 哦,原来是Tarjan算法的另外一个应用叫做支配树 我 ...
- HDU 1010 Tempter of the Bone【DFS经典题+奇偶剪枝详解】
Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Othe ...
- hdu 1848 Fibonacci again and again (初写SG函数,详解)
思路: SG函数的应用,可取的值为不连续的固定值,可用GetSG求出SG,然后三堆数异或. SG函数相关注释见代码: 相关详细说明请结合前一篇博客: #include<stdio.h> # ...
- hdu 1253 胜利大逃亡 (代码详解)解题报告
胜利大逃亡 Problem Description Ignatius被魔王抓走了,有一天魔王出差去了,这可是Ignatius逃亡的好机会. 魔王住在一个城堡里,城堡是一个A*B*C的立方体,可以被表示 ...
- hdu 1556 Color the ball (线段树+代码详解)
Color the ball Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T ...
- HDOJ Important Sisters
Important Sisters Time Limit: 7000/7000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Othe ...
随机推荐
- Handler, AsyncTask用法简单示例
package com.jim.testapp; import android.app.Activity; import android.os.AsyncTask; import android.os ...
- 【JZOJ4824】【NOIP2016提高A组集训第1场10.29】配对游戏
题目描述 流行的跳棋游戏是在一个有m*n个方格的长方形棋盘上玩的.棋盘起初全部被动物或障碍物占满了.在一个方格中,'X'表示一个障碍物,一个'0'-'9'的个位数字表示一个不同种类的动物,相同的个位数 ...
- JDK 8 中包列表及介绍
了解了Java 8中所有包的作用,对Java 8有了一个整体的了解,另外也是提高了自身的阅读能力.本文列出了Java 8中所有的包,并且对每一个包的功能做了简要的说明,希望对你有所帮助. ------ ...
- LeetCode103 Binary Tree Zigzag Level Order Traversal
Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to ...
- Kubernetes1.3新特性:新的资源回收控制器
(一) 核心概念 在kubernetes1.3中新增了一个资源回收控制器GarbaseCollector,用这个控制器来替代kubernetes1.3中的资源回收控制器GC. 如下为kubernet ...
- @loj - 2250@ 「ZJOI2017」仙人掌
目录 @题目描述@ @solution@ @accepted code@ @details@ @题目描述@ 如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌.所谓简单环 ...
- HZOJ visit
对于前30%的数据,可以考虑dp,f[i][j][k]表示时间为i,在i,j位置的方案数,枚举转移即可.要注意的是可以走到矩阵外. 对于另外30%数据,考虑推一下式子,设向右走y步,左z,上s,下x. ...
- Laravel5.1 实现第三方登录认证教程之 - 微信登录
https://laravel-china.org/topics/2451/laravel51-implementation-of-the-third-party-login-authenticati ...
- PHP中__FUNCTION__与__METHOD__的区别
你知道php中__FUNCTION__与__METHOD__的区别吗?本文通过一个小例子,为大家介绍下二者的区别,有兴趣的朋友可以参考下. PHP中__FUNCTION__与__METHOD__的 ...
- hdu 3272 Mission Impossible
Mission Impossible Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...