【TC SRM 710 DIV1】 Hyperboxes(FMT)(搜索)

传送门


题解:

两个高维立方图形相交,当且仅当在每一维都相交。

发现对于某个维度的相交情况,每一维是相同的。

所以我们算出一维的情况后FMT即可。

直接算很扯,考虑将所有端点排序,发现我们可以枚举端点的排列顺序。

注意到有的端点可能重合,我们需要枚举哪些端点重合。

也就是,我们需要直接枚举哪些端点是不一样的,分别在哪个组里面,然后对于一个端点的分组和偏序,我们爆搜有多少种划分方式满足它就行了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using namespace std;

cs int mod=998244353;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}

class Hyperboxes{
	private:
		static cs int N=15,SIZE=1<<16|1;
		int n,m,k,t,tot,S;
		int C[N],id[N][N],idg[N][N];
		int g[SIZE],f[SIZE];
		int a[N],gr[N],l[N],r[N],vis[N];
		
		void pre_dfs(int cur,int gcnt){
			if(cur==2*m){
				for(int re i=0;i<2*m;++i)
				(a[i]<m?l[a[i]]:r[a[i]-m])=gr[i];
				
				for(int re i=0;i<m;++i)if(l[i]==r[i])return ;
				
				for(int re i=0;i<m;++i)
				for(int re j=i+1;j<m;++j)
				if(l[i]==l[j]&&r[i]>=r[j])return ;
				
				int st=0;
				for(int re i=0;i<m;++i)
				for(int re j=i+1;j<m;++j)
				if(r[i]>=l[j])st|=1<<idg[i][j];
				
				Inc(g[st],C[gcnt+1]);
				return ;
			}
			for(int re i=0;i<m;++i)if(!vis[i]){
				a[cur]=i,vis[i]=true;
				gr[cur]=gcnt+1;
				pre_dfs(cur+1,gcnt+1);
				if(cur&&a[cur]>a[cur-1]){
					gr[cur]=gcnt;
					pre_dfs(cur+1,gcnt);
				}
				vis[i]=false;
				break;
			}
			for(int re i=0;i<m;++i)if(vis[i]&&!vis[i+m]){
				a[cur]=i+m;vis[i+m]=true;
				gr[cur]=gcnt+1;
				pre_dfs(cur+1,gcnt+1);
				if(cur&&a[cur]>a[cur-1]){
					gr[cur]=gcnt;
					pre_dfs(cur+1,gcnt);
				}
				vis[i+m]=false;
			}
		}
		
		inline void dfs(int cur){
			if(cur==t){
				for(int re i=0;i<m;++i)if(!vis[i])return ;
				for(int s=0,S=(1<<(m*(m-1)>>1));s<S;++s)if(g[s]){
					int st=0;
					for(int re i=0;i<t;++i)
					for(int re j=i+1;j<t;++j)
					if(gr[i]==gr[j]||(s&(1<<idg[gr[i]][gr[j]])))st|=1<<id[i][j];
					Inc(f[st],g[s]);
				}
				return ;
			}
			for(int re j=0;j<m;++j){
				gr[cur]=j;++vis[j];
				dfs(cur+1);--vis[j];
			}
		}
		
		inline int solve(){
			C[0]=1;for(int re i=1;i<=m*2;++i)C[i]=mul(C[i-1],n-i+1),Mul(C[i],power(i,mod-2));
						
			for(int re i=0;i<m;++i)
			for(int re j=i+1;j<m;++j)id[i][j]=id[j][i]=tot++;
			t=m;
			for(m=1;m<=t;++m){
				int tp=0;
				for(int re i=0;i<m;++i)
				for(int re j=i+1;j<m;++j)
				idg[i][j]=idg[j][i]=tp++;
				memset(g,0,sizeof g);
				pre_dfs(0,-1);
				dfs(0);
			}S=1<<tot;
			for(int re i=1;i<S;i<<=1)
			for(int re j=0;j<S;j+=i<<1)
			for(int re k=0;k<i;++k)Inc(f[j+k],f[i+j+k]);
			
			for(int re i=0;i<S;++i)f[i]=power(f[i],k);
			
			for(int re i=1;i<S;i<<=1)
			for(int re j=0;j<S;j+=i<<1)
			for(int re k=0;k<i;++k)Dec(f[j+k],f[i+j+k]);
			
			return f[0];
		}
		
	public:
		Hyperboxes(){}
		int findCount(int _n,int _m,int _k){
			n=_n,m=_m,k=_k;return solve();
		}
};

#ifdef zxyoi

using namespace std;
Hyperboxes Solver;

signed main(){
	cerr<<Solver.findCount(1000,3,1)<<"\n";
	return 0;
}

#endif
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值