1. 程式人生 > >Luogu P2597 [ZJOI2012]災難

Luogu P2597 [ZJOI2012]災難

但是 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]災難