[2020多校联考]MC
Solution
求概率。概率很好算,假设 \(X_i\) 所在连通块总生物数量为 \(S\),所有智商为 \(Y_i\) 的生物数为 \(s\)。那么概率就是
\[\binom{S}{N_i}\times\binom{s}{N_i}^{-1}
\]
在模 \(19260817\) 意义下的值,后面的求一下逆元就可以了。
(注意:当 \(S\) 或 \(s\) 其中之一小于 \(N_i\) 的话,概率为 \(0\))
那么这就转换为了一道计数题,显然是需要数据结构。维护连通块大小可以用并查集,维护连通块内某一种值的个数可以对每一个连通块维护一棵平衡树。对于操作一,直接在平衡树里加。对于操作二,因为并查集不支持分裂操作,按套路,可以倒着模拟这个过程,把分裂转为合并。只要首先预处理出删完边后的图和所有平衡树最后的情况,再倒过来处理询问。把操作一转换为减,操作二直接平衡树启发式合并,操作三直接查询即可。复杂度 \(O(n\log^2 n)\)
Tips
在平衡树内,对于相同 \(Y\) 值的点要合并成一个点,注意要把其中一个点的所有信息继承在另一个点上,否则会出现信息缺失的情况。在节点回收的时候要把其信息清空,之后才能用。
然后启发式合并判断大小时不要写反了
NOIP不知道能不能用 \(time.h\) 啊,反正今天模拟赛一发交上去RE了,好像是 \(time()\) 的问题。
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define N 200007
#define ll long long
#define Mod 19260817
#define lid ls[id]
#define rid rs[id]
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
ll qpow(ll x,ll y){
ll ret=1,cnt=0;
while(y>=(1LL<<cnt)){
if(y&(1LL<<cnt)) ret=(ret*x)%Mod;
x=(x*x)%Mod; cnt++;
}
return ret;
}
bool tag[N];
int n,m,Q,X[N],Y[N],fa[N];
int rt[N],key[N],sz[N],ls[N],rs[N],cnt=0,c[N];
ll s[N],S[N];
inline int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
inline int New(int x,int y){
key[++cnt]=rand();
sz[cnt]=1;
ls[cnt]=rs[cnt]=0;
S[cnt]=s[cnt]=y,c[cnt]=x;
return cnt;
}
inline void update(int id){
sz[id]=sz[lid]+sz[rid]+1;
S[id]=S[lid]+S[rid]+s[id];
}
void split(int id,int k,int &x,int &y){
if(!id) x=y=0;
else{
if(c[id]<=k) x=id,split(rid,k,rid,y);
else y=id,split(lid,k,x,lid);
update(id);
}
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(key[x]<key[y]){
rs[x]=merge(rs[x],y);
update(x); return x;
}else{
ls[y]=merge(x,ls[y]);
update(y); return y;
}
}
void uni(int &u,int v){
if(ls[v]) uni(u,ls[v]);
if(rs[v]) uni(u,rs[v]);
int x,y,z;
split(u,c[v],x,y);
split(x,c[v]-1,x,z);
ls[v]=rs[v]=0; sz[v]=1,S[v]=s[v];
if(!z) u=merge(merge(x,v),y);
else s[z]+=s[v],S[z]+=S[v],u=merge(merge(x,z),y);
}
void print(int id){
if(lid) print(lid);
printf("(%d,%lld) ",id,S[id]);
if(rid) print(rid);
}
int ty[N],A[N],B[N],C[N],T[N];
ll ans[N],qp[N*10];
int main(){
freopen("mc.in","r",stdin);
freopen("mc.out","w",stdout);
qp[0]=1;
for(int i=1;i<N*10;i++)
qp[i]=(qp[i-1]*i)%Mod;
srand(19260817);
n=read(),m=read(),Q=read();
for(int i=1;i<=n;i++){
int x=read(),y=read();
rt[i]=New(y,x),fa[i]=i;
}
for(int i=1;i<=m;i++)
X[i]=read(),Y[i]=read();
for(int i=1;i<=Q;i++){
ty[i]=read();
if(ty[i]==2) T[i]=read(),tag[T[i]]=1;
else{
A[i]=read(),B[i]=read(),C[i]=read();
if(ty[i]==1){
int u=find(A[i]),x,y,z;
split(rt[u],C[i],x,y);
split(x,C[i]-1,x,z);
if(!z) rt[u]=merge(merge(x,New(C[i],B[i])),y);
else s[z]+=B[i],S[z]+=B[i],rt[u]=merge(merge(x,z),y);
}
}
}
for(int i=1;i<=m;i++){
if(tag[i]) continue;
int fa_x=find(X[i]),fa_y=find(Y[i]);
if(fa_x==fa_y) continue;
if(sz[fa_x]>sz[fa_y]) swap(fa_x,fa_y);
uni(rt[fa_y],rt[fa_x]);
fa[fa_x]=fa_y;
}
for(int i=Q;i;i--){
if(ty[i]==1){
int u=find(A[i]),x,y,z;
split(rt[u],C[i],x,y);
split(x,C[i]-1,x,z);
s[z]-=B[i],S[z]-=B[i];
rt[u]=merge(merge(x,z),y);
}else if(ty[i]==2){
int fa_x=find(X[T[i]]),fa_y=find(Y[T[i]]);
if(fa_x==fa_y) continue;
if(sz[fa_x]>sz[fa_y]) swap(fa_x,fa_y);
uni(rt[fa_y],rt[fa_x]);
fa[fa_x]=fa_y;
}else{
int u=find(A[i]),x,y,z;
ll ret=S[rt[u]];
split(rt[u],C[i],x,y);
split(x,C[i]-1,x,z);
if(c[z]==C[i]&&ret>=B[i]&&s[z]>=B[i])
ans[i]=(qp[s[z]]*qp[ret-B[i]]%Mod)*qpow(qp[ret]*qp[s[z]-B[i]]%Mod,Mod-2)%Mod;
else ans[i]=0;
rt[u]=merge(merge(x,z),y);
}
}
for(int i=1;i<=Q;i++)
if(ty[i]==3) printf("%lld\n",ans[i]);
}