[ZJOI2012]灾难
题解:
首先很容易想到拓扑排序,但是会发现一个节点会由多个决定
但是我们还不知道他们的关系
那么如果我们知道了决定它的那些节点的关系
我们就可以做了
所以就先拓扑排序,按照拓扑顺序进行
然后 我们需要找到最后面的那个能决定所有决定这个节点的那个点(有点绕)
这个可以用倍增来维护(其实就是lca)
最后统计答案就统计一下子树大小就可以了
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5;
int cd[N],head[N],p[N],cnt,lca;
int bz[N][20],dep[N],count2[N],l,n,m;
int b[N];
bool ff[N];
struct re{
int a,b;
}a[N*100];
void arr(int x,int y)
{
a[++l].a=head[x];
a[l].b=y;
head[x]=l;
}
int get_lca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for (int i=18;i>=0;i--)
if (dep[bz[x][i]]>=dep[y])
x=bz[x][i];
if (x==y) return (x);
for (int i=18;i>=0;i--)
if (bz[x][i]!=bz[y][i])
x=bz[x][i],y=bz[y][i];
return (bz[x][0]);
}
void dfs(int x)
{
int u=head[x];
while (u)
{
int v=a[u].b;
dfs(v);
u=a[u].a;
count2[x]+=count2[v];
}
count2[x]++;
}
queue<int> q;
int main()
{
freopen("noip.in","r",stdin);
freopen("noip.out","w",stdout);
std::ios::sync_with_stdio(false);
cin>>n;
for (int i=1;i<=n;i++)
{
while (cin>>m&&m)
{
cd[m]++; arr(i,m);
}
}
for (int i=1;i<=n;i++)
if (cd[i]==0) q.push(i);
while (!q.empty())
{
int x=q.front(); q.pop();
p[++cnt]=x;
int u=head[x],v,cnt2=0;
while (u)
{
v=a[u].b; cd[v]--;
if (!cd[v]) q.push(v);
cnt2++;
u=a[u].a;
}
if (cnt2==1) bz[x][0]=v;
if (cnt2==0) bz[x][0]=0,dep[x]=1;
}
for (int i=n;i;i--)
{
int xx=p[i];
int u=head[xx],cnt2=0,lca=0;
while (u)
{
int x=a[u].b;
b[++cnt2]=x;
u=a[u].a;
}
if (cnt2>=1)
{
lca=b[1];
for (int i=2;i<=cnt2;i++)
{
lca=get_lca(lca,b[i]);
}
bz[xx][0]=lca; dep[xx]=dep[lca]+1;
}
for (int i=1;i<=18;i++)
bz[xx][i]=bz[bz[xx][i-1]][i-1];
}
memset(head,0,sizeof(head)); l=0;
for (int i=1;i<=n;i++)
{
if (bz[i][0])
arr(bz[i][0],i);
else q.push(i);
}
while (!q.empty())
{
int x=q.front(); q.pop();
dfs(x);
}
for (int i=1;i<=n;i++) cout<<count2[i]-1<<endl;
return 0;
}