BZOJ2815&&洛谷P2597 [ZJOI2012]災難
阿新 • • 發佈:2018-12-17
拓撲+LCA
思路清奇的一道好題
這個題真的不毒瘤!!
吐槽一下出題人如何同時嚇死草原上的羊??
先考慮一下暴力
反向建圖跑個拓撲就完事了
考慮一些特殊的情況
假如輸入的是一棵樹,那麼一個點的貢獻就是他的子樹大小-1,也就是子樹中除他以外的所有點,但是大多數情況輸入是一個DAG,那我們是不是可以找到一種方法,把它轉化成一棵樹呢? 我們考慮樣例的情況 不難發現,若是4號點涼了,當且僅當2,3都涼了,而2,3都涼了的情況得建立在1涼了的情況下,也就是說4只會對1涼了這種情況產生貢獻,所以我們就可以直接把4掛到1下面,這樣就不用管2,3對4的影響了,然後我們考慮 ,對於1個點指向的多個點,1會產生貢獻,只有在它的食物,也就是他指向的這一堆點全部涼了的情況下,而這一堆點都涼了,那必然他們的食物也涼了,一直向上推,直到他們只有一個食物,也就是說所有點交在了一起時,這個交點涼了,那麼下面也就都涼了,這個點是啥?
是下面所有點在新樹上的LCA,但發現有些點沒有食物?我們額外建一個0號點,使得所有沒沒有食物的點的fa=0,這樣就會交於一點了 ,我們在原圖上跑拓撲,按拓撲序的倒序開始建樹,因為他的拓撲序越靠後,那麼他在新樹上的深度就越淺,因為他被最多的人指了,必然在食物鏈最底端,然後我們倒序遍歷就相當於正序建樹了,我們把一個點接到他指向的所有點的LCA上就好了,而他指向的所有點,必然比他先處理了,最後我們dfs求一遍子樹大小就好了
程式碼
//By AcerMo
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=100500;
int n;
int to[M],nxt[M],head[M],cnt;
int net[M],go[M],last[M],tot;
int f[M][20],dep[M],ti[M],in[M],ans[M];
inline void read(int &x)
{
x=0;char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit (ch)) x=x*10+ch-'0',ch=getchar();
return ;
}
inline void ad1(int x,int y)
{
to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;
return ;
}
inline void ad2(int x,int y)
{
go[++tot]=y;net[tot]=last[x];last[x]=tot;
return ;
}
inline void topsort()
{
queue<int>q;int t=0;
for (int i=1;i<=n;i++)
if (!in[i]) q.push(i);
while (q.size())
{
int u=q.front();q.pop();ti[++t]=u;
for (int i=head[u];i;i=nxt[i])
if (--in[to[i]]==0) q.push(to[i]);
}
return ;
}
inline int lca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for (int i=18;i>=0;i--)
if (dep[f[x][i]]>=dep[y])
x=f[x][i];
if (x==y) return x;
for (int i=18;i>=0;i--)
if (f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
inline void built()
{
dep[0]=1;
memset(last,-1,sizeof(last));
for (int i=n;i>=1;i--)
{
int u=ti[i];
if (head[u]==0)
{ad2(0,u);dep[u]=2;continue;}
int l=to[head[u]];
for (int k=nxt[head[u]];k;k=nxt[k])
l=lca(l,to[k]);ad2(l,u);
dep[u]=dep[l]+1;f[u][0]=l;
for (int k=1;k<=18;k++)
f[u][k]=f[f[u][k-1]][k-1];
}
return ;
}
inline void dfs(int x)
{
ans[x]=1;
for (int i=last[x];~i;i=net[i])
dfs(go[i]),ans[x]+=ans[go[i]];
return ;
}
signed main()
{
read(n);int x;
for (int i=1;i<=n;i++)
while (1)
{
read(x);if (!x) break;
in[x]++;ad1(i,x);
}
topsort();built();dfs(0);
for (int i=1;i<=n;i++)
printf("%d\n",ans[i]-1);
return 0;
}