Luogu P2597 [ZJOI2012]災難
阿新 • • 發佈:2018-06-06
但是 for 技術 div 不能 AD freopen 為我 uil
一道非常綜合的好題然後就莫名其妙地知道了動態LCA的求法
果然是ZJOI的題目,只能說這思路服了
首先我們發現每次操作只會滅絕一種動物,然後我們想一下就知道如果有\(n(n>=2)\)個食物的動物就不會滅絕。
然後我們YY一個叫滅絕樹的東西,在這個樹上的點都滿足一個性質:當一個節點被割去時,以它為根的整棵子樹對應的動物都會滅亡。
然後我們只需要考慮如何搞出這個樹了。
然後我們再YY一個虛擬節點0,讓它向所有生產者連邊,我們可以形象得理解成太陽。
然後我們將題目中的樹反向建邊,然後就變成了這樣:
然後我們發現5號點很難受,因為有兩個點連向它。那麽如果只割一個點怎麽讓5號點滅絕。
答案很簡單:只需要讓它的兩個父節點都滅絕即可
然後我們看要讓2,3號點都滅絕,那麽:
只需要讓1滅絕即可。那麽1與2,3的關系就很簡單了:1是2,3的LCA
那麽就很好辦了,我們只需要按順序找出每一個點的所有父節點的LCA,然後在滅絕樹上把這個點掛在它的LCA下面即可。
但是這個順序怎麽辦,我們註意到題目中提到了:
這個圖沒有環。
然後就我們從0開始拓撲排序即可確定這個順序,然後就只求LCA了
這裏註意因為我們沒有剛開始就把圖建出來,所以DFS序+RMQ和Tarjan的方法就不能用了。
所以我們用倍增,每次將一個點掛上樹時然後單獨更新它的LCA信息
然後就可以解決了,不得不吐槽一下這道題開了三個鄰接表,還是很狗的,因為這個調了很久
CODE
#include<cstdio>
#include<cstring>
using namespace std;
const int N=70000,P=20;
struct edge
{
int to,next;
}e[N<<2],re[N<<2],new_e[N<<1];
int head[N],rhead[N],new_head[N],dep[N],ru[N],q[N],father[N][P],size[N],tot,sum,cnt,rcnt,new_cnt,n,x,rt;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch=tc();
while (ch<‘0‘||ch>‘9‘) ch=tc();
while (ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=tc();
}
inline void write(int x)
{
if (x/10) write(x/10);
putchar(x%10+‘0‘);
}
inline void add(int x,int y)
{
e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
}
inline void radd(int x,int y)
{
re[++rcnt].to=y; re[rcnt].next=rhead[x]; rhead[x]=rcnt;
}
inline void new_add(int x,int y)
{
new_e[++new_cnt].to=y; new_e[new_cnt].next=new_head[x]; new_head[x]=new_cnt;
}
inline void swap(int &a,int &b)
{
int t=a; a=b; b=t;
}
inline void top_sort(void)
{
register int i; int H=0,T=0;
for (i=1;i<=n;++i)
if (!ru[i]) q[++T]=i,father[i][0]=rt,dep[i]=1,new_add(rt,i),new_add(i,rt);
while (H<T)
{
int now=q[++H];
for (i=head[now];i!=-1;i=e[i].next)
if (!(--ru[e[i].to])) q[++T]=e[i].to;
}
}
inline void rebuild(int now)
{
for (register int i=0;i<P-1;++i)
if (father[now][i]^-1) father[now][i+1]=father[father[now][i]][i];
}
inline int LCA(int x,int y)
{
register int i;
if (dep[x]<dep[y]) swap(x,y);
for (i=P-1;i>=0;--i)
if (father[x][i]^-1&&dep[father[x][i]]>=dep[y]) x=father[x][i];
if (!(x^y)) return x;
for (i=P-1;i>=0;--i)
if (father[x][i]^-1&&father[y][i]^-1&&father[x][i]^father[y][i]) x=father[x][i],y=father[y][i];
return father[x][0];
}
inline void get_tree(void)
{
register int i,j;
for (i=1;i<=n;++i)
{
int now=q[i];
if (!(father[now][0]^-1))
{
int fa=-1;
for (j=rhead[now];j!=-1;j=re[j].next)
if (fa^-1) fa=LCA(fa,re[j].to); else fa=re[j].to;
if (!(fa^-1)) fa=0;
father[now][0]=fa; new_add(fa,now); new_add(now,fa); dep[now]=dep[fa]+1; rebuild(now);
}
}
}
inline void DFS(int now,int fa)
{
size[now]=1;
for (register int i=new_head[now];i!=-1;i=new_e[i].next)
if (new_e[i].to^fa) DFS(new_e[i].to,now),size[now]+=size[new_e[i].to];
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i; read(n); memset(father,-1,sizeof(father));
memset(e,-1,sizeof(e)); memset(head,-1,sizeof(head));
memset(re,-1,sizeof(re)); memset(rhead,-1,sizeof(rhead));
memset(new_e,-1,sizeof(new_e)); memset(new_head,-1,sizeof(new_head));
for (i=1;i<=n;++i)
for (read(x);x;read(x))
add(x,i),radd(i,x),++ru[i];
top_sort(); get_tree(); DFS(rt,-1);
for (i=1;i<=n;++i)
write(size[i]-1),putchar(‘\n‘);
return 0;
}
Luogu P2597 [ZJOI2012]災難