您的位置:首页 > 其它

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

2016-03-18 09:51 411 查看
【题意】

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

【思路】

构造一棵“灭绝树”,要求这棵树满足如果删除根节点后则该子树内的所有结点都不可达。则答案为子树大小-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+10;
const int M = 2e6+10;
const int D = 21;

ll read() {
char c=getchar();
ll f=1,x=0;
while(!isdigit(c)) {
if(c=='-') f=-1; c=getchar();
}
while(isdigit(c))
x=x*10+c-'0',c=getchar();
return x*f;
}

struct Edge {
int v,nxt;
}e[M][2];
int en[2]={1,1},front
[2];
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
,fa
[D],dep
,siz
;
queue<int> q;
vector<int> tp;

void topo()
{
FOR(i,1,n) if(!in[i])
q.push(i);
while(!q.empty()) {
int u=q.front(); q.pop();
tp.push_back(u);
trav(u,i,0) {
int v=e[i][0].v;
if(!(--in[v])) {
q.push(v);
}
}
}
}
int lca(int u,int v)
{
if(u==-1) return v;
if(dep[u]<dep[v]) swap(u,v);
int t=dep[u]-dep[v];
FOR(i,0,D-1)
if(t&(1<<i)) u=fa[u][i];
if(u==v) return u;
for(int i=D-1;i>=0;i--)
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
void build_tree()
{
for(int i=tp.size()-1;i>=0;i--) {
int u=tp[i];
int lc=-1;
trav(u,i,0)
lc=lca(lc,e[i][0].v);
lc=lc==-1?0:lc;
adde(lc,u,1);
dep[u]=dep[lc]+1;
fa[u][0]=lc;
FOR(i,1,D-1) fa[u][i]=fa[fa[u][i-1]][i-1];
}
}
int vis
;
void getans(int u,int fa)
{
tp.clear();
q.push(0);
while(!q.empty()) {
int u=q.front(); q.pop();
tp.push_back(u);
trav(u,i,1) {
int v=e[i][1].v;
if(!vis[v])
vis[v]=1,q.push(v);
}
}
for(int i=tp.size()-1;i>=0;i--) {
int u=tp[i];
siz[u]=1;
trav(u,i,1)
siz[u]+=siz[e[i][1].v];
}
}
int main()
{
//freopen("in.in","r",stdin);
//freopen("out.out","w",stdout);
n=read();
int x;
FOR(i,1,n) {
while(x=read(),x) {
adde(i,x,0); in[x]++;
}
}
topo();
build_tree();
getans(0,-1);
FOR(i,1,n) printf("%d\n",siz[i]-1);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: