您的位置:首页 > 运维架构

hdu 5420 Victor and Proposition(强连通+线段树建图)

2015-08-25 03:13 225 查看
题意:给出一棵树,每个点都给一个x,d,代表它和x这个点以及以下深度差小于d的点都有一条有向边,求互相可达的u,v对数。

做法:可以很容易发觉如果我们把点连好然后直接找强连通分量,然后答案就是每个强连通分量大小x*(x-1)/2之和。

但是因为点有10000个,边极限可以出到n^2个的。所以我们得简化边的个数。先处理出dfs序,保存一下每个点子树的dfs序范围,可以发现,x以及它之下深度小于d的点其实在x子树的dfs序范围里的,我们如果能把这些点按照深度排序,自然就能得到连续的想要的点,基于这个思想。我们考虑对dfs序建立线段树。区间[l,r]的vector代表的dfs序标号为l,r范围内的点,这些点是个二元组,除了是谁还得保存下深度,按之前所说我们把它们按照深度排序,我们把这些排序后的点建新的点来建图类似这样
1<—2 <— 3 <—4,再把这些新的点与原来它代表的点连边,这样的话,如果我想要的d范围最大到3(由于排序了,可以二分找),那么我就跟3连一条边,这样就小于d的点也都连上了。

至于限制条件是必须在x的子树dfs序范围内这个问题,我们可以通过类似线段树区间更新的方式来解决。由于有了线段树,任何dfs区间一定是由不超过logn个区间组合得到,所以就按照刚才的方式递归建边即可。

最后再来算一下空间和时间。我们要新建的点数等于线段树中每个vector的size和。假设某一层为i,那么它要存的点个数是2^(dep-i)个(dep是最大深度),这一层点的个数是2^i个,两者相乘就是2^dep个,即n个,也就是说每一层需要n的空间,树高为logn,所以点数就是o(nlogn)的,边数的话,由于我们需要把这些点之间连边,以及每个新建的点要和原来连边,所以还要乘一个2。

由于子树每个区间都是有序的,我们可以用归并排序,所以建树复杂度o(nlogn),tarjan复杂度o(n+m) = o(n+nlogn)所以总复杂度o(nlogn)。

PS:由于本弱是仔细研读了昂神的代码才理解的。所以写的比较像来着。另。merge竟然比我手写的归并要快。这是为啥。。

AC代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<string.h>
#include<string>
#include<sstream>
#include<bitset>
using namespace std;
#define ll __int64
#define ull unsigned __int64
#define eps 1e-8
#define NMAX 1000000000
#define MOD (1<<30)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI acos(-1)
template<class T>
inline void scan_d(T &ret)
{
char c;
int flag = 0;
ret=0;
while(((c=getchar())<'0'||c>'9')&&c!='-');
if(c == '-')
{
flag = 1;
c = getchar();
}
while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
if(flag) ret = -ret;
}
const int maxn = 100000+10;
const int maxm = 100000*30+10;
struct node
{
int dep,pos;
node(){}
node(int _d, int _p):dep(_d),pos(_p){}
bool operator < (const node &t) const
{
if(dep != t.dep) return dep < t.dep;
return pos < t.pos;
}
};

int head[maxm],ecnt;
struct Edge
{
int v,next;
}e[maxm<<1];

void add_edge(int u, int v)
{
e[ecnt].v = v; e[ecnt].next = head[u];
head[u] = ecnt++;
}

int st[maxn],ed[maxn],dep[maxn],dfs_clock,n,nct;
int a[maxn];
void dfs(int u, int d)
{
st[u] = ++dfs_clock;
a[dfs_clock] = u;
dep[u] = d;
for(int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].v;
dfs(v,d+1);
}
ed[u] = dfs_clock;
}

vector<node>v[maxn<<2];
int beg[maxn<<2];

void combin(vector<node> &x, vector<node> &y, vector<node> &z)
{
int sz1 = x.size(), sz2 = y.size();
int p = 0, p1 = 0, p2= 0;
while(p1 < sz1 || p2 < sz2)
{
if(p1 == sz1 || (p2 != sz2 && y[p2] < x[p1])) z[p++] = y[p2++];
else z[p++] = x[p1++];
}
}

void build(int l, int r, int rt)
{
v[rt].resize(r-l+1);
if(l == r)
{
v[rt][0] = node(dep[a[l]],a[l]);
n++;
add_edge(n,a[l]);
beg[rt] = n;
return;
}
int mid = l+r >> 1;
build(lson);
build(rson);
// combin(v[rt<<1],v[rt<<1|1],v[rt]);
merge(v[rt<<1].begin(),v[rt<<1].end(),v[rt<<1|1].begin(),v[rt<<1|1].end(),v[rt].begin());
beg[rt] = n+1;
for(int i = 1; i < r-l+1; i++)
add_edge(n+i+1,n+i);
for(int i = 0; i < r-l+1; i++)
add_edge(n+i+1,v[rt][i].pos);
n += r-l+1;
}

void update(int L, int R, int x, int d, int l, int r, int rt)
{
if(L <= l && R >= r)
{
int pos = lower_bound(v[rt].begin(), v[rt].end(), node(d+1,0))-v[rt].begin()-1;
if(pos >= 0) add_edge(x,beg[rt]+pos);
return;
}
int mid = l+r >> 1;
if(L <= mid) update(L,R,x,d,lson);
if(R > mid) update(L,R,x,d,rson);
}

int pre[maxm],lowlink[maxm],sum[maxm],scc_cnt;
bool sccno[maxm];
stack<int>S;
void dfs(int u)
{
pre[u] = lowlink[u] = ++dfs_clock;
S.push(u);
for(int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].v;
if(!pre[v])
{
dfs(v);
lowlink[u] = min(lowlink[u],lowlink[v]);
}
else if(!sccno[v])
{
lowlink[u] = min(lowlink[u],pre[v]);
}
}
if(lowlink[u] == pre[u])
{
++scc_cnt;
while(1)
{
int x = S.top(); S.pop();
sccno[x] = 1;
sum[scc_cnt] += x <= nct;
if(x == u) break;
}
}
}

void find_scc()
{
dfs_clock = scc_cnt = 0;
memset(pre,0,sizeof(pre));
memset(sccno,0,sizeof(sccno));
memset(sum,0,sizeof(sum));
for(int i = 1; i <= nct; i++) if(!pre[i])
dfs(i);
ll ans = 0;
for(int i = 1; i <= scc_cnt; i++)
ans += (ll)sum[i]*(sum[i]-1)/2;
printf("%I64d\n",ans);
}

int main()
{
#ifdef GLQ
freopen("input.txt","r",stdin);
// freopen("o.txt","w",stdout);
#endif
int _t;
scanf("%d",&_t);
while(_t--)
{
scanf("%d",&n);
nct = n;
memset(head,-1,sizeof(head));
ecnt = 0;
for(int i = 2; i <= n; i++)
{
int v;
scanf("%d",&v);
add_edge(v,i);
}
dfs_clock = 0;
dfs(1,0);
memset(head,-1,sizeof(head));
ecnt = 0;
build(1,nct,1);
for(int i = 1; i <= nct; i++)
{
int x,d;
scanf("%d%d",&x,&d);
update(st[x],ed[x],i,dep[x]+d,1,nct,1);
}
find_scc();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: