1. 程式人生 > >2018.10.23【校內模擬】戰爭(並查集)

2018.10.23【校內模擬】戰爭(並查集)

傳送門

解析:

首先,順序刪邊維護連通性其實就是倒著把所有點加回去就行了。

同時並查集維護一下當前連通塊權值之和。

每次合併得到的新增的貢獻就是兩個連通塊權值的乘積。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(
c=gc()));num=c^48; while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48); return num; } inline void outint(ll a){ static char ch[23]; if(a==0)pc('0'); while(a)ch[++ch[0]]=a-a/10*10,a/=10; while(ch[0])pc(ch[ch[0]--]^48); } cs int N=1000006; cs ll mod=1000000007; struct edge{ int u,v,tim; friend bool operator
<(cs edge &a,cs edge &b){ return a.tim<b.tim; } }e[N]; int del[N]; int fa[N]; ll val[N]; inline int getfa(int x){ while(x^fa[x])x=fa[x]=fa[fa[x]]; return x; } ll now; inline void merge(int u,int v){ u=getfa(u); v=getfa(v); if(u==v)return ; fa[v]=u; now=(now+val[u]*val[v])%mod;
val[u]=(val[u]+val[v])%mod; } ll ans[N]; int n,m; signed main(){ n=getint(); m=getint(); for(int re i=1;i<n;++i){ e[i].u=i+1; e[i].v=getint(); } for(int re i=1;i<=n;++i)val[i]=i,fa[i]=i; memset(del,0x3f,sizeof del); for(int re i=1;i<=m;++i){ int k=getint(); while(k--){ int u=getint(); del[u]=i; } } for(int re i=1;i<n;++i)e[i].tim=min(del[e[i].u],del[e[i].v]); sort(e+1,e+n); int k=n-1; for(int re i=m+1;i;--i){ while(e[k].tim>=i) merge(e[k].u,e[k].v),--k; ans[i]=now; } for(int re i=1;i<=m+1;++i)outint(ans[i]),pc('\n'); return 0; }