GZOI2025 Day1 T2 count
GZOI2025 Day1 T2 count
题意
还没有补题链接。
我常常追忆过去。题目中文名忘了。
有 \(n\) 个数,求所有满足(至少 \(k\) 个子集 \(S\) 满足 \(\sum_{i \in S} a_i = w\))的 \(w\) 的异或和。
\(n \le 200, a_i \le 5 \times 10^4,k \le 7\)。
时限 \(0.5s\)。
思路
发现 \(\sum a_i = 10^7\),如果复杂度接近线性刚好可以 \(0.5s\) 内通过。
朴素的背包 DP 是 \(O(n \sum a_i)\) 的。
显然我们要抓住 \(k\) 很小来优化。
考虑我们的背包是怎么做的,\(f_i\) 表示和为 \(i\) 的方案数。我们加一个数 \(a\),就令 \(f'_{i+a} \gets f_{i}\)。
于是不难想到使用 bitset 做一个位移然后按位或的操作。
我不知道为什么我在场上会认为按位或的操作复杂度是长度平方的?好神秘啊
但是 \(k>1\) 咋办。
这个是才应该是本题难点吧。
直接模拟吗?开 \(7\) 个 bitset \(b_i\),\(b_{i,j}\) 为 \(1\) 表示 \(f_j=i\)。
然后时间复杂度 \(O(k^2 n \frac{\sum a_i}{w})\),大概 \(10^9\)。常数一般。但是真能过吗?
然后翻阅其他选手的代码,终于学会了妙妙做法。感谢 GD-024 大蛇!
注意到一个 \(f_i\) 至多 += \(k\) 次(需要保证每次至少 \(+1\))。所以我们只需要保证我们每次 += 都是有效的。
我们开 \(3\) 个 bitset 分别记录 \(f_i < k\)(需要被 +=)的 \(i\),\(f_i>0\)(可以使 += 有效)的 \(i\),\(f_i \ge k\)(作为答案)的 \(i\)。
注意 \(0<f_i<k\) 不好统计,但是 \(0 < f_i < k = f_i < k \cap f_i >0\)。
发现 \(f_i < k, f_i \ge k\) 互为补集。
然后具体做法详见代码。
时间复杂度 \(O(k \sum a_i + n\frac{\sum a_i}{w})\)。
我觉得这个做法好妙妙啊。为啥大家都能想到,只有我想不到?
我并没有完全相信 bitset 真的可以作为正解存在,也许下次会相信了。你为什么宁愿相信它是一道卡常题也不去多想一会儿 bitset 做法?
热知识,bitset 是 5 级算法。

code
由于没有地方补题,而我也懒得手搓数据,所以假装这个代码是对的吧。
#include<bits/stdc++.h>
#define sf scanf 
#define pf printf 
#define rep(x,y,z) for(int x=y;x<=z;x++) 
#define per(x,y,z) for(int x=y;x>=z;x--) 
using namespace std;
typedef long long ll;
namespace wing_heart {
	constexpr int N=2e2+7,W=5e4+7,K=7,mx=5e4;
	int n,k;
	int a[N];
	int ans;
	int f[N*W];
	bitset<N*W> f1,fk,f0;
	int s[N*W];
	void main() {
		sf("%d%d",&n,&k);
		rep(i,1,n) sf("%d",&a[i]);
		f[0]=1;
		f1[0]=1;
		if(k==1) fk[0]=1;
		rep(i,1,n) {
			f0=~fk;
			f0&=(f0&f1)<<a[i];
			f1|=f1<<a[i];
			fk|=fk<<a[i];
			int c=0;
			for(int i=f0._Find_first();i<=n*mx;i=f0._Find_next(i)) s[++c] = i; 
			while(c) {
				if(f[s[c]] += f[s[c]-a[i]] >=k) fk[s[c]]=1;
				--c;
			}
		}
		for(int i=fk._Find_first();i<=n*mx;i=fk._Find_next(i)) ans^=i;
		pf("%d\n",ans);
	}
}
int main() {
	#ifdef LOCAL 
	freopen("count3.in","r",stdin);
//	freopen("in.txt","r",stdin);
	freopen("my.out","w",stdout);
	#else 
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	#endif 
	wing_heart :: main();
}
本文来自博客园,作者:wing_heart,转载请注明原文链接:https://www.cnblogs.com/wingheart/p/19091671

                
            
        
浙公网安备 33010602011771号