YbtOJ-毒瘤染色【LCT】

1|0正题


1|1题目大意

开始时有一张n个点没有边的图,q次操作加入一条边,如果加入后图是一个沙漠(只有边仙人掌的图)时才能够加入。

每次加入后询问:开始所有点都是白色,k次随机挑一个点染黑,求最后白色点的连通块数和黑色点的连通块数的和。

强制在线

1n105,1q3×105,1k109


1|2解题思路

因为强制在线肯定需要用LCT维护,至于仙人掌我们维护在环上的边就好了。

然后考虑怎么求答案。

仙人掌的连通块数=点数-边数+环数。

至于本题我们可以考虑这些东西存在的期望数量。

wi表示指定i个点是白点的概率,那么显然就是(nin)k

然后设bi表示指定i个点是黑点的概率,我们考虑容斥指定一些点是白点就是

bi=j=0i(ij)(1)j(njn)k

这样是O(i)的,但是其实我们只有求环的出现概率时这个值会很大,但是环的大小和不超过n,所以可以在需要的时候暴力求。

时间复杂度:O(qlogn)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<stack> #define ll long long using namespace std; const ll N=2e5+10,P=998244353; struct LCT{ ll fa[N],t[N][2],siz[N]; bool w[N],v[N],lazy[N],r[N],p[N],hp[N]; stack<ll> s; bool Nroot(ll x) {return fa[x]&&(t[fa[x]][0]==x||t[fa[x]][1]==x);} bool Direct(ll x) {return t[fa[x]][1]==x;} void PushUp(ll x){ siz[x]=siz[t[x][0]]+siz[t[x][1]]+(!p[x]); w[x]=(p[x]&v[x])|w[t[x][0]]|w[t[x][1]]; hp[x]=p[x]|hp[t[x][0]]|hp[t[x][1]]; return; } void Rev(ll x) {r[x]^=1;swap(t[x][0],t[x][1]);return;} void Rvy(ll x){w[x]=hp[x];lazy[x]=v[x]=1;return;} void PushDown(ll x){ if(r[x])Rev(t[x][0]),Rev(t[x][1]),r[x]=0; if(lazy[x]){ if(t[x][0])Rvy(t[x][0]); if(t[x][1])Rvy(t[x][1]); lazy[x]=0; } PushUp(x);return; } void Rotate(ll x){ ll y=fa[x],z=fa[y]; ll xs=Direct(x),ys=Direct(y); ll w=t[x][xs^1]; if(Nroot(y))t[z][ys]=x; t[y][xs]=w;t[x][xs^1]=y; if(w)fa[w]=y;fa[x]=z;fa[y]=x; PushUp(y);PushUp(x);return; } void Splay(ll x){ ll y=x;s.push(x); while(Nroot(y))y=fa[y],s.push(y); while(!s.empty())PushDown(s.top()),s.pop(); while(Nroot(x)){ y=fa[x]; if(!Nroot(y))Rotate(x); else if(Direct(x)==Direct(y)) Rotate(y),Rotate(x); else Rotate(x),Rotate(x); } return; } void Access(ll x){ for(ll y=0;x;y=x,x=fa[x]) Splay(x),t[x][1]=y,PushUp(x); return; } void MakeRoot(ll x) {Access(x);Splay(x);Rev(x);return;} void Link(ll x,ll y) {MakeRoot(x);fa[x]=y;Access(x);return;} void Split(ll x,ll y) {MakeRoot(x);Access(y);Splay(y);return;} }T; ll n,q,k,op,ans,fa[N],inv[N],fac[N],fnv[N]; ll find(ll x) {return (fa[x]==x)?x:(fa[x]=find(fa[x]));} ll power(ll x,ll b){ ll ans=1; while(b){ if(b&1)ans=ans*x%P; x=x*x%P;b>>=1; } return ans; } ll C(ll n,ll m) {return fac[n]*fnv[m]%P*fnv[n-m]%P;} ll W(ll x) {return power((n-x)*inv[n]%P,k);} ll B(ll x){ ll ans=0; for(ll i=0;i<=x;i++){ ll w=power((n-i)*inv[n]%P,k); w=w*C(x,i)%P;(ans+=w*((i&1)?-1:1))%=P; } return (ans+P)%P; } signed main() { freopen("graph.in","r",stdin); freopen("graph.out","w",stdout); inv[1]=fnv[0]=fac[0]=1; for(ll i=2;i<N;i++)inv[i]=P-inv[P%i]*(P/i)%P; for(ll i=1;i<N;i++)fnv[i]=fnv[i-1]*inv[i]%P,fac[i]=fac[i-1]*i%P; scanf("%lld%lld%lld%lld",&n,&q,&k,&op); for(ll i=1;i<=n;i++)fa[i]=i,T.siz[i]=1; ll w2=W(2),b2=B(2),las=0; ans=(W(1)*n+op*B(1)*n)%P; int cnt=n; while(q--){ ll x,y; scanf("%lld%lld",&x,&y); x^=las;y^=las; if(find(x)!=find(y)){ fa[find(x)]=find(y); ++cnt;T.p[cnt]=T.hp[cnt]=1; T.Link(x,cnt); T.Link(y,cnt); (ans-=w2+op*b2)%=P; } else if(x!=y){ T.Split(x,y); if(!T.w[y]){ T.w[y]=T.v[y]=T.lazy[y]=1; ll S=T.siz[y]; (ans+=W(S)-w2)%=P; if(op)(ans+=B(S)-b2)%=P; } } ans=(ans+P)%P; printf("%lld\n",las=ans); } return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15869452.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2021-02-07 P4338-[ZJOI2018]历史【LCT】
2021-02-07 P3760-[TJOI2017]异或和【树状数组】
2021-02-07 P3273-[SCOI2011]棘手的操作【线段树,并查集】
2021-02-07 P4424-[HNOI/AHOI2018]寻宝游戏【结论】
2021-02-07 P4606-[SDOI2018]战略游戏【圆方树,虚树】
点击右上角即可分享
微信分享提示