bzoj 2815 [ZJOI2012]灾难(构造,树形DP)

时间:2023-03-08 18:34:51

【题意】

求把每个点删除后,不可达点的数目。

【思路】

构造一棵“灭绝树”,要求这棵树满足如果删除根节点后则该子树内的所有结点都不可达。则答案为子树大小-1。

如何构造这棵“灭绝树”?

将原图拓扑排序。当我们处理u的时候保证对u的所有食物已经建好树。引入0号节点,以之为所有生产者的食物。设u的食物为v[0..k],当我们至少把v[0..k]的LCA删掉之后u会灭绝,因此由LCA向u连边。增量构造LCA所需信息dep,fa。

 鉴于写dfs的时候发生了一些奇奇怪怪的事(包括卡了下bzoj的评测机 lol,就把dfs改成了bfs

【题解链接】

http://fanhq666.blog.163.com/blog/static/8194342620124274154996/

【代码】

 #include<set>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define trav(u,i,f) for(int i=front[u][f];i;i=e[i][f].nxt)
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
using namespace std; typedef long long ll;
const int N = 1e5+;
const int M = 2e6+;
const int D = ; ll read() {
char c=getchar();
ll f=,x=;
while(!isdigit(c)) {
if(c=='-') f=-; c=getchar();
}
while(isdigit(c))
x=x*+c-'',c=getchar();
return x*f;
} struct Edge {
int v,nxt;
}e[M][];
int en[]={,},front[N][];
void adde(int u,int v,int f)
{
e[++en[f]][f]=(Edge){v,front[u][f]}; front[u][f]=en[f];
} int n;
int in[N],fa[N][D],dep[N],siz[N];
queue<int> q;
vector<int> tp; void topo()
{
FOR(i,,n) if(!in[i])
q.push(i);
while(!q.empty()) {
int u=q.front(); q.pop();
tp.push_back(u);
trav(u,i,) {
int v=e[i][].v;
if(!(--in[v])) {
q.push(v);
}
}
}
}
int lca(int u,int v)
{
if(u==-) return v;
if(dep[u]<dep[v]) swap(u,v);
int t=dep[u]-dep[v];
FOR(i,,D-)
if(t&(<<i)) u=fa[u][i];
if(u==v) return u;
for(int i=D-;i>=;i--)
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][];
}
void build_tree()
{
for(int i=tp.size()-;i>=;i--) {
int u=tp[i];
int lc=-;
trav(u,i,)
lc=lca(lc,e[i][].v);
lc=lc==-?:lc;
adde(lc,u,);
dep[u]=dep[lc]+;
fa[u][]=lc;
FOR(i,,D-) fa[u][i]=fa[fa[u][i-]][i-];
}
}
int vis[N];
void getans(int u,int fa)
{
tp.clear();
q.push();
while(!q.empty()) {
int u=q.front(); q.pop();
tp.push_back(u);
trav(u,i,) {
int v=e[i][].v;
if(!vis[v])
vis[v]=,q.push(v);
}
}
for(int i=tp.size()-;i>=;i--) {
int u=tp[i];
siz[u]=;
trav(u,i,)
siz[u]+=siz[e[i][].v];
}
}
int main()
{
//freopen("in.in","r",stdin);
//freopen("out.out","w",stdout);
n=read();
int x;
FOR(i,,n) {
while(x=read(),x) {
adde(i,x,); in[x]++;
}
}
topo();
build_tree();
getans(,-);
FOR(i,,n) printf("%d\n",siz[i]-);
return ;
}