题面
分析
正难则反系列
删除操作很难维护,倒过来,加边维护。并查集同时维护这个集合的祖先和点权和。
合并两个集合的代价就是 sum[x]*sum[y]
但是要注意最后不一定把所有点删完了,所以最后一次删除后的答案要dfs一下联通块特殊求
【然而出题人并没有卡这个..】
代码
- #include<bits/stdc++.h>
- using namespace std;
- #define N 1101000
- #define ll long long
- const ll mod=1e9+7;
- ll n,m,k,cnt,pos;
- ll fa[N],sum[N],ans[N],vis[N],mark[N],first[N];
- vector<ll>v[N];
- struct email
- {
- ll u,v;
- ll nxt;
- }e[N*4];
- inline ll find(ll x){return (fa[x]==x)?x:fa[x]=find(fa[x]);}
- inline void add(ll u,ll v)
- {
- e[++cnt].nxt=first[u];first[u]=cnt;
- e[cnt].u=u;e[cnt].v=v;
- }
- template<class T>
- void read(T &x)
- {
- x=0;
- static char c=getchar();
- while(c<'0'||c>'9') c=getchar();
- while(c>='0'&&c<='9')
- x=x*10+c-'0',c=getchar();
- }
- inline void merge(int x,int y)
- {
- pos=(pos+(sum[x]*sum[y])%mod)%mod;
- fa[y]=x;sum[x]+=sum[y];sum[x]%=mod;
- }
- inline void dfs(ll u)
- {
- vis[u]=1;
- for(ll i=first[u];i;i=e[i].nxt)
- {
- ll v=e[i].v;
- if(vis[v]||mark[v])continue;
- dfs(v);
- ll fax=find(u),fay=find(v);
- merge(fax,fay);
- }
- }
- int main()
- {
- read(n);read(m);
- for(ll u=2;u<=n;u++)
- {
- ll v;
- read(v);
- add(u,v);add(v,u);
- }
- for(ll i=1;i<=n;i++)sum[i]=i,fa[i]=i;
- for(ll i=1;i<=m;i++)
- {
- ll p;read(k);
- for(ll j=0;j<k;j++)
- read(p),mark[p]=1,v[i].push_back(p);
- }
- for(ll i=1;i<=n;i++)
- if(mark[i]==0&&vis[i]==0)
- dfs(i);
- ans[m+1]=pos;
- for(ll x=m;x>=1;x--)
- {
- for(ll j=0;j<v[x].size();j++)
- {
- ll u=v[x][j];
- ll fax=find(u);
- for(int i=first[u];i;i=e[i].nxt)
- {
- int v=e[i].v;
- if(mark[v])continue;
- ll fay=find(v);
- merge(fax,fay);
- }
- mark[u]=0;
- }
- ans[x]=pos;
- }
- for(ll i=1;i<=m+1;i++)
- printf("%lld ",ans[i]);
- }