灾难树 一般的做法 先拓扑 再建树 求lca确定father 根对于子树中所有点都是灾难点
BZOJ 2815
以下是fhq神犇的题解
《灾难》
【题意简述】
给一个有向无环图,问每个节点删掉之后会导致多少个点不可达。
【算法描述】
有这样一个事实:
生物之间的灭绝的结构形成了一个树,树上的一个节点的灭绝会且仅会导致以它为根的子树的灭绝。我们管这个树叫“灭绝树”。
对于生产者,我们给它添加一个假想的食物:太阳。
这样,“灭绝树”就形成了一个以太阳为根的树。
下面说明如何通过增量法把灭绝树建出来,同时也是对灭绝树的存在性的证明。
首先,把食物网按从猎物到捕食者的顺序拓扑排序。
之后,依次考虑每个生物i.我们已经构建好了排序在i之前的生物组成的“灭绝树”了。
假设i的食物有x[0]~x[k](x[0]~x[k]在拓扑排序中比i靠前)。
很显然,只有x[0]~x[k]在树上的公共祖先的灭绝会导致i灭绝,否则i一定可以找到能让它活下来的食物。
...
/
/
LCA
/|\
_/ ||
/ | \
O | \
/ \ \ \
x x x x
i
于是,我们可以把i挂在x[0]~x[k]的最近公共祖先下面。
处理完所有的生物,我们得到的树就是整个图的灭绝树了。
一旦得到灭绝树,每个生物的灾难值就可以通过以它为根的子树的大小减1来计算.
【复杂度分析】
拓扑排序的时间复杂度是O(|E|)的。
一共有|E|次LCA的查询,和|V|次添加边的操作。
我们使用某种支持快速查询LCA、添加点的数据结构(例如动态树)。
这样,总的时间复杂度是O(|E|log|V|)。
动态树什么的,我还是用倍增。。
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<cstring>
#define V G[p].v
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x)
{
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
struct edge{
int u,v;
int next;
};
edge G[5000005];
int head[70005],num;
inline void add(int u,int v,int p)
{
G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}
int n;
int lst[70005],pnt,vst[70005];
inline void dfs(int u)
{
vst[u]=1;
for (int p=head[u];p;p=G[p].next)
if (!vst[V])
dfs(V);
lst[pnt--]=u;
}
int parent[70005][25],depth[70005];
inline int LCA(int u,int v)
{
if (depth[u]<depth[v]) swap(u,v);
for (int k=20;k>=0;k--)
if (((depth[u]-depth[v])>>k)&1)
u=parent[u][k];
if (u==v) return u;
for (int k=20;k>=0;k--)
if (parent[u][k]!=parent[v][k])
u=parent[u][k],v=parent[v][k];
return parent[u][0];
}
vector<int> food[70005];
int size[70005];
inline void count(int u)
{
size[u]=1;
for (int p=head[u];p;p=G[p].next)
count(V),size[u]+=size[V];
}
int main()
{
int tmp;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(n);
for (int i=1;i<=n;i++)
{
read(tmp);
if (tmp)
for (;tmp;read(tmp))
food[i].push_back(tmp),add(tmp,i,++num);
else
food[i].push_back(n+1),add(n+1,i,++num);
}
pnt=++n;
dfs(n);
cl(G); cl(head); num=0;
depth[n]=1;
for (int i=2;i<=n;i++)
{
int lca=0,u=lst[i];
for (int j=0;j<food[u].size();j++)
if (lca==0)
lca=food[u][j];
else
lca=LCA(lca,food[u][j]);
add(lca,u,++num);
depth[u]=depth[lca]+1;
parent[u][0]=lca;
for (int k=1;k<=20;k++)
parent[u][k]=parent[parent[u][k-1]][k-1];
}
count(n);
for (int i=1;i<n;i++)
printf("%d\n",size[i]-1);
return 0;
}
BZOJ 2851
不用拓扑 这里求的是一些个点的灾难点的并集
就是求一些点到根的路径的并 注意去重
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<cstring>
#define V G[p].v
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x)
{
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
struct edge{
int u,v;
int next;
};
edge G[500005];
int head[200005],num;
inline void add(int u,int v,int p)
{
G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}
int n;
int parent[200005][25],depth[200005];
inline int LCA(int u,int v)
{
if (depth[u]<depth[v]) swap(u,v);
for (int k=20;k>=0;k--)
if (((depth[u]-depth[v])>>k)&1)
u=parent[u][k];
if (u==v) return u;
for (int k=20;k>=0;k--)
if (parent[u][k]!=parent[v][k])
u=parent[u][k],v=parent[v][k];
return parent[u][0];
}
vector<int> food[200005];
vector<int> query[200005];
int last[200005],ans[200005];
inline void dfs(int u)
{
for (int i=0;i<query[u].size();i++)
{
int q=query[u][i];
if (!last[q])
ans[q]+=depth[u]-1;
else
ans[q]+=depth[u]-depth[LCA(u,last[q])];
last[q]=u;
}
for (int p=head[u];p;p=G[p].next)
dfs(V);
}
int main()
{
int tmp,tot,Q;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(n);
for (int i=1;i<=n;i++)
{
read(tot);
if (tot)
while (tot--)
read(tmp),food[i].push_back(tmp);
else
food[i].push_back(n+1);
}
++n;
depth[n]=1;
for (int i=1;i<n;i++)
{
int lca=0,u=i;
for (int j=0;j<food[u].size();j++)
if (lca==0)
lca=food[u][j];
else
lca=LCA(lca,food[u][j]);
add(lca,u,++num);
depth[u]=depth[lca]+1;
parent[u][0]=lca;
for (int k=1;k<=20;k++)
parent[u][k]=parent[parent[u][k-1]][k-1];
}
read(Q);
for (int i=1;i<=Q;i++)
{
read(tot);
while (tot--)
read(tmp),query[tmp].push_back(i);
}
dfs(n);
for (int i=1;i<=Q;i++)
printf("%d\n",ans[i]);
return 0;
}