bitset 求解高维偏序

菜,题简单,trick 蠢,求别骂。

记录今天做题的时候遇到的一个小 trick。

先看一道题:P3810 【模板】三维偏序(陌上花开)

平凡的三维偏序板子,相信大家都会用 CDQ/树套树/K-D tree 之类的优秀做法秒了吧!

然后看这个题:求五维偏序,n3×104,保证每一维这 n 个数都是 n 的排列。

我会套三层 CDQ 做到 O(nlog4n)!我还会 K-D tree 做到 O(n74)

然后看这个题:CF1826E. Walk the Runway

题意有点复杂,但是就是需要求 m 维偏序,m500n5000,保证每一维都 n

显然 CDQ 并不可取,如果直接暴力也是 O(n2m) 的,K-D tree 应该也不行(我不会)。怎么办?这里介绍一种使用 bitset 的简单做法。

有一个 n×n 的矩阵 AAi,j 为 1 表示 j 每一维都严格小于 i。枚举每一维 j,排一遍序可以求出在这一维上严格小于第 i 个物品的集合,记为 Si,j。显然 Ai,j 为 1 当且仅当 k[1,m] 都有 jSi,k,而这个过程其实就是 bitset 求并。于是我们就做到了时间复杂度 O(mnlogn+mn2w),空间复杂度 O(n2w)m 维偏序。

还可以优化吗?

考虑这道题:m 维偏序,n5×104保证每一维这 n 个数都是 n 的排列,空间 64MB

空间爆了,怎么办?考虑分块优化空间。我们对于每一维进行值域上的分块,块长 b=n。定义 fi,j 表示第 i 维,这一维的值落在值域上 [1,j×b] 这一区间的点集,gi,j 表示第 i 维值为 j 的点。显然这个可以 O(mnn) 预处理。询问的时候依旧是整块取现成,暴力遍历散块。

这样做的话时间复杂度依旧是 O(mnn+mn2w),但是空间优化到了 O(mnnw)(注意这道题可以这样做是因为保证每一维这 n 个数都是 n 的排列,如果只是像上一道题一样保证 n 则复杂度并不正确)。

附一个 CF1826E 的代码。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct edge{
	int v,nxt;
}e[25000005];
int tot,head[5005],deg[5005];
void add(int u,int v){
	e[++tot]=(edge){v,head[u]},head[u]=tot,deg[v]++;
}
int n,m,b[5005],p[5005];ll a[5005],f[5005];
int cmp(int x,int y){
	return b[x]<b[y];
}
bitset<5005>res[5005];
void solve(){
	m=read(),n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(i!=j)res[i][j]=1;
	for(int j=1;j<=m;j++){
		for(int i=1;i<=n;i++)b[i]=read(),p[i]=i;
		sort(p+1,p+n+1,cmp);bitset<5005>tmp;
		for(int i=1;i<=n;i++){
			int pos=i;while(pos<=n&&b[p[pos]]==b[p[i]])res[p[pos]]&=tmp,pos++;
			pos--;for(int k=i;k<=pos;k++)tmp[p[k]]=1;
			i=pos;
		}
	} 
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(res[i][j])add(i,j);
	queue<int>q;for(int i=1;i<=n;i++)if(!deg[i])q.push(i),f[i]=a[i];
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].v;f[v]=max(f[v],f[u]+a[v]);
			if((--deg[v])==0)q.push(v);
		}
	}
	ll ans=0;
	for(int i=1;i<=n;i++)ans=max(ans,f[i]);
	printf("%lld\n",ans);
}
signed main(){
	int T=1;
	while(T--){
		solve();
	}
	return 0;
}

reference:几道很Interesting的偏序问题 - yybbitset 求解高维偏序

posted @   xx019  阅读(241)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示