[CF1641D]Two Arrays

137 篇文章 1 订阅
23 篇文章 0 订阅

题目

传送门 to CF

思路

做过类似的题,有试图容斥。即 [ S = ∅ ] = ∑ T ⫅ S ( − 1 ) ∣ T ∣ [S=\varnothing]=\sum_{T\subseteqq S}(-1)^{|T|} [S=]=TS(1)T,二项式反演,用这个式子翻译 “交集为空”。

本来考虑 min-max \text{min-max} min-max 反演,而后发现不对。

最后还是要 回归原问题的性质:求最小 w i + w j w_i+w_j wi+wj 。所以我们只需找到最小的 w j w_j wj二分 后变为判定性问题,只需数个数。于是线段树上二分, s e t \tt set set 维护桶,复杂度 O ( n 2 m log ⁡ 2 n ) \mathcal O(n2^m\log^2n) O(n2mlog2n),空间复杂度 O ( n 2 m log ⁡ n ) \mathcal O(n2^m\log n) O(n2mlogn) 。先是 M L E \rm MLE MLE,后来又 T L E \rm TLE TLE,确实卡不过去。

很可惜,我对原问题的性质的发掘还不足——找的是全局最小值,而非每个 w i w_i wi 都必须求解最小 w j w_j wj 。所以可以 双指针。时间复杂度 O ( n 2 m log ⁡ n ) \mathcal O(n2^m\log n) O(n2mlogn),空间复杂度 O ( n 2 m ) \mathcal O(n2^m) O(n2m)

然而这仍然不能通过!该死的 CF \textrm{CF} CF 这都要卡是吧!必须把 log ⁡ n \log n logn 给搞掉。最简单的方式是 unordered   map \texttt{unordered map} unordered map,只要你的哈希值足够微妙就可以通过(如果是 CF \textrm{CF} CF 就要冒着被 h a c k \rm hack hack 的风险,除非你使用随机哈希)。复杂但正确的方式是, t r i e \tt trie trie 维护

直觉上,时空复杂度皆为 O ( n 2 m m ) \mathcal O(n2^mm) O(n2mm) 。可仔细研究后发现,将 m m m 个元素的每个子集都插入 t r i e \tt trie trie O ( 2 m ) \mathcal O(2^m) O(2m) 的(查询复杂度同理),即每次找到 l o w b i t \tt lowbit lowbit,去掉 l o w b i t \tt lowbit lowbit 对应的子集已经在 t r i e \tt trie trie 中,直接在其后接上 l o w b i t \tt lowbit lowbit 就行。时间、空间都是 O ( 2 m ) \mathcal O(2^m) O(2m) 。(可是儿子数组还是要用 unordered   map \texttt{unordered map} unordered map 维护……)

代码

为了避免代码过长,这里使用了哈希版

#include <cstdio> // JZM yydJUNK!!!
#include <iostream> // XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // (the STRONG long for LONELINESS)
#include <cctype> // ZXY yydSISTER!!!
#include <unordered_map> // I hate this motherf**ker
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

typedef unsigned long long ullong;
const unsigned RAD = 19260817;
ullong getHash(int a[], int len){
	ullong v = 0;
	for(int i=0; i!=len; ++i)
		v = RAD*v+unsigned(a[i]);
	return v;
}

const int MAXN = 100005, MAXM = 5;
int n, m, a[MAXN][MAXM+1];

ullong s[1<<MAXM]; int len, cnt[1<<MAXM];
void got(int id){
	static int sta[MAXM], top;
	for(int S=1; S!=(1<<m); ++S){
		for(int j=top=0; j!=m; ++j) if(S>>j&1)
			sta[top ++] = a[id][j];
		s[S-1] = getHash(sta,top);
	}
}

std::unordered_map<ullong,int> mp;
void insert(){ for(int j=0; j!=len; ++j) ++ mp[s[j]]; }
void erase(){ for(int j=0; j!=len; ++j) -- mp[s[j]]; }
inline int query(){
	int now = 0;
	for(int i=0; i!=len; ++i){
		if(!mp.count(s[i])) continue;
		if(cnt[i]&1) now += mp[s[i]];
		else now -= mp[s[i]];
	}
	return now;
}

bool cmp(const int &x, const int &y){
	return a[x][m] < a[y][m];
}
int id[MAXN];
int main(){
	n = readint(), m = readint();
	rep(i,1,n) rep(j,0,m) a[i][j] = readint();
	len = (1<<m)-1; // fixed value
	for(int S=1; S!=(1<<m); ++S)
		cnt[S-1] = __builtin_popcount(S);
	rep(i,1,n){
		std::sort(a[i],a[i]+m);
		got(i), insert(); // insert all
	}
	rep(i,1,n) id[i] = i;
	std::sort(id+1,id+n+1,cmp);
	int ans = 0x7fffffff;
	for(int *i=id+1,j=n; i!=id+n+1; ++i)
		for(got(*i); query()!=j; --j,got(*i)){
			ans = std::min(ans,a[id[j]][m]+a[*i][m]);
			got(id[j]), erase(); // remove
		}
	if(ans == 0x7fffffff) ans = -1;
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值