洛谷P5592 美德的讲坛

对于 x,存在 p,满足 2px<2p+1,对于所有 aiai2p 的值分组,得每组内的两两异或值都 <x

然后就只需考虑组与组之间的情况了,将其转化为最小割,源点向第一个组的每个点连容量为 1 的边,第二组的每个点向汇点连容量为 1 的边,两组间异或和 x 的点对连容量为 的边。

直接跑最小割复杂度无法接受,先将最小割转为最大流,对所有权值建立 01 Trie

a0a1a 的左右子树,solve(a,b) 为以 a,b 为根的子树的最大匹配,得:

x 在对应深度为 1 时,得其值为 solve(a0,b1)+solve(a1,b0)

x 在对应深度为 0 时,进行分类讨论:

|a0|<|b1||a1|<|b0||a||a0|>|b1||a1|>|b0||b||a0|>|b1||a1|<|b0|min(solve(a0,b0),|b0||a1|,|a0||b1|)+|a1|+|b1||a0|<|b1||a1|>|b0|min(solve(a1,b1),|b1||a0|,|a1||b0|)+|a0|+|b0|

还需注意 a=b 的情况,设 v0=solve(a0,a0),v1=solve(a1,a1),其值为 v0+v1+min(|a0|v0,|a1|v1)

因为每次修改的节点个数为 O(logn),所以每次询问记忆化即可。

#include<bits/stdc++.h> #define maxn 100010 #define maxm 10000010 #define s(x,k) siz[ch[x][k]] using namespace std; typedef long long ll; template<typename T> inline void read(T &x) { x=0;char c=getchar();bool flag=false; while(!isdigit(c)){if(c=='-')flag=true;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} if(flag)x=-x; } int n,q,tot=1,root=1; ll lim; int ch[maxm][3],siz[maxm],val[maxm],dep[maxm]; ll a[maxn]; bool vis[maxm]; void insert(ll x,int v) { int p=root; siz[p]+=v,dep[p]=60; for(int i=60;i>=0;--i) { int c=(x>>i)&1; vis[p]=false; if(!ch[p][c]) ch[p][c]=++tot; siz[p=ch[p][c]]+=v,dep[p]=i-1; } } int solve(int x,int y) { if(!siz[x]||!siz[y]) return 0; if(vis[x]&&vis[y]) return val[x]; vis[x]=vis[y]=true; if(dep[x]==-1) return val[x]=min(siz[x],siz[y]); if((lim>>dep[x])&1) return val[x]=solve(ch[x][0],ch[y][1])+solve(ch[x][1],ch[y][0]); int v0=solve(ch[x][0],ch[y][0]),v1=solve(ch[x][1],ch[y][1]); if(x==y) return val[x]=v0+v1+min(s(x,0)-v0,s(x,1)-v1); if(s(x,0)<=s(y,1)&&s(x,1)<=s(y,0)) return val[x]=siz[x]; if(s(x,0)>=s(y,1)&&s(x,1)>=s(y,0)) return val[x]=siz[y]; if(s(x,0)>=s(y,1)&&s(x,1)<=s(y,0)) return val[x]=min(v0,min(s(y,0)-s(x,1),s(x,0)-s(y,1)))+s(x,1)+s(y,1); if(s(x,0)<=s(y,1)&&s(x,1)>=s(y,0)) return val[x]=min(v1,min(s(y,1)-s(x,0),s(x,1)-s(y,0)))+s(x,0)+s(y,0); } int main() { read(n),read(q),read(lim); for(int i=1;i<=n;++i) read(a[i]),insert(a[i],1); printf("%d\n",max(n-solve(root,root),1)); while(q--) { int x; read(x),insert(a[x],-1); read(a[x]),insert(a[x],1); printf("%d\n",max(n-solve(root,root),1)); } return 0; }

__EOF__

本文作者lhm_
本文链接https://www.cnblogs.com/lhm-/p/14239364.html
关于博主:sjzez 的一名 OI 学生
版权声明:转载标明出处
声援博主:希望得到宝贵的建议
posted @   lhm_liu  阅读(231)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示