jzoj5987 【WC2019模拟2019.1.4】仙人掌毒题 (动态圆方树维护仙人掌)

86 篇文章 0 订阅
28 篇文章 0 订阅

在这里插入图片描述

在这里插入图片描述

失智

又被题目吓到了
考虑树的情况,连通块数目就是总点数-存在的边。
考虑仙人掌的情况,连通块数目就是总点数-存在的边+存在的环
因为是0/1分开考虑,所以所谓存在的X就是要求相关联的点颜色一样。

使用lct维护圆方树就可以了。
算一个环都变黑的概率可以容斥,因为总环中点数是不超过n+m的,暴力就可以了。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int mo = 998244353,N=4e5+10;
typedef long long ll;
int n,m,t,w;
ll jc[N],njc[N],ans;

int fa[N],c[N][2],tot,rtag[N],s[N],sv[N];
#define isroot(x) (c[fa[x]][0]!=(x)&&c[fa[x]][1]!=(x))
#define lr(x) (c[fa[x]][1]==(x))
#define upd(x) (s[x]=s[c[x][0]]|s[c[x][1]]|sv[x])

ll C(ll n,ll m) {
   if (n<m) return 0; return jc[n] * njc[m] % mo * njc[n-m] % mo;
}

ll ksm(ll x,ll y) {
   x%=mo;
   ll ret = 1; for (;y;y>>=1) {
   	if (y&1) ret=ret*x%mo;
   	x=x*x%mo;
   }
   return ret;
}

void rotate(int x) {
   int y=fa[x],z=lr(x);
   c[y][z]=c[x][1-z];
   if (c[x][1-z]) fa[c[x][1-z]]=y;

   if (!isroot(y)) c[fa[y]][lr(y)]=x;
   fa[x]=fa[y];

   fa[y]=x;
   c[x][1-z]=y;
   upd(y);
}


void rev(int x){
   if (x){
   	rtag[x]^=1;swap(c[x][0],c[x][1]);
   }
}

void down(int x){
   if (rtag[x]){
   	rtag[x]=0;
   	rev(c[x][0]),rev(c[x][1]);
   }
}

int Q[N];
void splay(int x) {
   int t=x; while (!isroot(t)) Q[++Q[0]]=t,t=fa[t];
   Q[++Q[0]]=t; while (Q[0]) down(Q[Q[0]--]);
   while (!isroot(x)) {
   	if (!isroot(fa[x])){
   		if (lr(x)==lr(fa[x])) rotate(fa[x]); else rotate(x);
   	}
   	rotate(x);
   }
   upd(x);
}

void access(int x) {
   for (int u=0;x;u=x,x=fa[x]){
   	splay(x);
   	c[x][1]=u;
   	upd(x);
   }
}

void mkroot(int x) {
   access(x),splay(x),rev(x);
}

void link(int x,int y){
   mkroot(x),access(x),splay(x);
   fa[x]=y;
}

void cut(int x,int y) {
   mkroot(x),access(y),splay(y);
   c[y][0]=0,fa[x]=0;
   upd(y);
}

bool cnet(int x,int y) {
   mkroot(x);
   splay(x);
   access(y),splay(y);
   return !isroot(x);
}

ll ds,ed,cir,invn,eee;
ll lis[N];
void go(int x) {
   if (!x) return;
   down(x); go(c[x][0]);
   lis[++lis[0]]=x;
   go(c[x][1]);
}

ll calcBlackCir(int sz) {
   ll ret = 0;
   for (int i = 0; i <= sz; i++) {
   	ret = (ret + ((i&1) ? -1 : 1) * C(sz, i) * ksm((n-i) * invn % mo,t) % mo) % mo;
   }
   return ret;
}

int main() {
   freopen("cactus.in","r",stdin);
   freopen("cactus.out","w",stdout);
   n=1e5,jc[0]=1; for (int i=1;i<=n;i++)jc[i]=jc[i-1]*i%mo;
   njc[n]=ksm(jc[n],mo-2); for (int i=n-1;~i;i--)njc[i]=njc[i+1]*(i+1)%mo;
   scanf("%d %d %d %d",&n,&m,&t,&w); tot=n;
   invn = ksm(n, mo - 2);
   ds = (w==0) ? n * ksm((n-1)*invn%mo,t) % mo : n;
   for(int ee=1;ee<=m;ee++){eee=ee;
   	int u,v; scanf("%d %d",&u,&v);
   	int flag=1;
   	if (cnet(u,v)) {
   		mkroot(u),access(v),splay(v);
   		if (s[v]==0) {
   			lis[0]=0;
   			go(v);
   			cir = (cir + ksm((n - lis[0]) * invn % mo, t)) % mo;
   			if (w) cir = (cir + calcBlackCir(lis[0])) % mo;
   			tot++; sv[tot]=1; upd(tot);
   			for (int i = 1; i < lis[0]; i++) {
   				cut(lis[i],lis[i+1]);
   				link(lis[i],tot);
   			}
   			link(lis[lis[0]],tot);
   		} else {
   			flag=0;
   		}
   	} else link(u,v);
   	if (flag) {
   		ed = (ed + ksm((n-2) * invn % mo, t))%mo;
   		if (w) ed = (ed + 1 - 2*ksm((n-1)*invn%mo, t) + ksm((n - 2)*invn%mo, t))%mo;
   	}
   	printf("%lld\n",((ds-ed+cir)%mo+mo)%mo);
   }
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值