题目
题目背景
一生二,二生三,三生万物。然何生一焉?唯
O
U
Y
E
\sf OUYE
OUYE 可之。
题目描述
求有多少个
S
⫅
[
1
,
n
]
∩
Z
S\subseteqq[1,n]\cap\Z
S⫅[1,n]∩Z 使得
[
1
,
n
]
∩
Z
∈
{
sum
(
T
)
∣
T
⫅
S
}
[1,n]\cap\Z\in\{\text{sum}(T)\;|\;T\subseteqq S\}
[1,n]∩Z∈{sum(T)∣T⫅S}
其中 sum ( S ) = ∑ x ∈ S x \text{sum}(S)=\sum_{x\in S}x sum(S)=∑x∈Sx 。输出对质数 M M M 取模, M M M 由输入给定。
数据范围与提示
n
⩽
5
×
1
0
5
n\leqslant 5\times 10^5
n⩽5×105,时限
3s
\text{3s}
3s 。提示:复杂度比较高。
思路
一眼鉴定为背包问题,然后我成了 🤡
其他题解都看不懂,只有这位老哥的给我讲懂了。话说 ZJOI \text{ZJOI} ZJOI 题解区也有他 🤔
恰好是
[
1
,
n
]
[1,n]
[1,n] 肯定是有性质的啊。至少联想一下
[
1
,
n
]
[1,n]
[1,n] 能凑出
[
1
,
n
(
n
+
1
)
2
]
[1,\frac{n(n+1)}{2}]
[1,2n(n+1)] 这种老百姓结论啊。从小到大排列
x
1
,
x
2
,
…
,
x
k
∈
S
x_1,x_2,\dots,x_k\in S
x1,x2,…,xk∈S,则只需
1
+
∑
i
=
1
α
−
1
x
i
⩾
x
α
(
α
∈
[
1
,
n
]
)
1+\sum_{i=1}^{\alpha-1}x_i\geqslant x_{\alpha}\quad(\alpha\in[1,n])
1+i=1∑α−1xi⩾xα(α∈[1,n])
证明比发现容易。于是立刻有 O ( n 2 ) \mathcal O(n^2) O(n2) 做法。优化可能要考虑根号分治,虽然这也是困难的。
对
{
x
i
}
\{x_i\}
{xi} 整体
+
1
+1
+1 时,很难保证上面的约束条件。必须把约束条件去掉——使用 容斥,减去不合法的情况。枚举恰好无法凑出的值,也就是说
[
1
,
v
]
[1,v]
[1,v] 中选择了的数的和是
(
v
−
1
)
(v{-}1)
(v−1),且
[
1
,
v
)
[1,v)
[1,v) 都可以被凑出。记方案数为
f
(
v
)
f(v)
f(v),则答案为
2
n
−
∑
i
=
1
n
2
n
−
i
f
(
i
)
2^n-\sum_{i=1}^{n}2^{n-i}f(i)
2n−i=1∑n2n−if(i)
现在只需求出
f
f
f 。显然它也得容斥递推。有
f
(
v
)
=
choose
(
0
,
v
−
1
)
−
∑
i
=
1
v
−
1
f
(
i
)
⋅
choose
(
i
,
v
−
i
)
f(v)=\text{choose}(0,v{-}1)-\sum_{i=1}^{v-1}f(i)\cdot\text{choose}(i,v{-}i)
f(v)=choose(0,v−1)−i=1∑v−1f(i)⋅choose(i,v−i)
其中 choose ( i , x ) \text{choose}(i,x) choose(i,x) 为从 ( i , + ∞ ) (i,+\infty) (i,+∞) 中选数使得和为 x x x 的方案数。这个东西好像是可以 O ( n 1.5 ) \mathcal O(n^{1.5}) O(n1.5) 搞定的,但是我们不可能求出所有 choose \text{choose} choose 啊?
注意到:为了使得 choose ≠ 0 \text{choose}\ne 0 choose=0,必须有 i < v 2 i<\frac{v}{2} i<2v 。毕竟 i = v i=v i=v 是不在范围内的。
又想到: choose \text{choose} choose 是背包问题,要与 f f f 合并,则可以将 f f f 作为初状态。
因此:可以倍增求出 f f f 。新开背包数组 h h h 初始为空。从小到大枚举 i ∈ [ 1 , n ] i\in[1,\sqrt{n}] i∈[1,n],做背包转移,同时将 f ( i ) f(i) f(i) “放入” 背包 h h h 。这就选择完了 ⩽ n \leqslant\sqrt{n} ⩽n 的数。然后从大到小枚举 i ⩽ n i\leqslant\sqrt{n} i⩽n 作为数字个数,同样地新开背包做转移,并每次将 h h h 和 f ( j ) ( j > n ) f(j)\;(j>\sqrt{n}) f(j)(j>n) 放入。
为了减小空间消耗,放入背包时 h ( j ) h(j) h(j) 应放至 ( j + i n ) (j{+}i\sqrt{n}) (j+in) 位置,即 > n >\sqrt{n} >n 的限制强制满足。而 f ( j ) f(j) f(j) 应放至 ( j + i j ) (j{+}ij) (j+ij) 位置,因为它的限制是 > j >j >j 。
由此 T ( n ) = T ( n 2 ) + O ( n n ) = O ( n n ) T(n)=T(\frac{n}{2})+\mathcal O(n\sqrt{n})=\mathcal O(n\sqrt{n}) T(n)=T(2n)+O(nn)=O(nn) 。
最后吐槽: n ⩽ 5 × 1 0 5 n\leqslant 5\times 10^5 n⩽5×105 就算 O ( n log 2 n ) \mathcal O(n\log^2 n) O(nlog2n) 我都觉得磕碜,然而你告诉我是 O ( n 1.5 ) \mathcal O(n^{1.5}) O(n1.5) 的?
代码
#include <cstdio>
#include <algorithm> // Almighty XJX yyds!!!
#include <cstring> // oracle: ZXY yydBUS!!!
#include <cctype> // Huge Egg-Dog devours me
using llong = long long;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
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*10+(c^48);
return a*f;
}
const int MAXN = 1<<19;
int mod, f[MAXN], g[MAXN], tmp[MAXN];
inline void modAddUp(int &x, const int &y){
if((x += y) >= mod) x -= mod;
}
int main(){
int n = readint(); mod = readint();
f[1] = 1; // valid states
for(int m=3,v=1; true; m=m<<1|1){
while((v+1)*(v+1) <= m) ++ v; // v = sqrt(m)
memset(tmp+1,0,m<<2), tmp[1] = mod-1;
rep(i,1,v){ // value (by column)
drep(j,m,i) modAddUp(tmp[j],tmp[j-i]);
modAddUp(tmp[i],f[i]); // into range
}
memset(g+1,0,m<<2); // clear
drep(i,v,1){ // how many (by row)
rep(j,1,m-i*v) modAddUp(g[j+i*v],tmp[j]);
rep(j,v+1,m-i*j) modAddUp(g[j+i*j],f[j]);
memmove(g+i,g,(m-i+1)<<2), memset(g,0,i<<2);
rep(j,0,m-i) modAddUp(g[j+i],g[j]); // knapsack
}
rep(j, (m>>1)+1, m) modAddUp(
f[j]=mod-tmp[j], mod-g[j]);
if(m >= n) break; // enough
}
int ans = 0, v = 1;
for(int i=n; i; --i,v=(v<<1)%mod)
ans = int((ans+llong(mod-v)*f[i])%mod);
modAddUp(ans,v); printf("%d\n",ans);
return 0;
}