题目
洛谷上根本交不起……建议直接去 CF \text{CF} CF上提交。
题目概要
给定集合
A
=
{
a
1
,
a
2
,
a
3
,
…
,
a
n
}
A=\{a_1,a_2,a_3,\dots,a_n\}
A={a1,a2,a3,…,an}和
m
m
m,求一个集合
P
=
{
p
1
,
p
2
,
p
3
,
…
,
p
k
}
P=\{p_1,p_2,p_3,\dots,p_k\}
P={p1,p2,p3,…,pk},满足这些条件:
- ∀ ⟨ t 1 , t 2 , t 3 , … , t k ⟩ ∈ N k , ∑ i = 1 k t i p i > m ∨ ∑ i = 1 k t i p i ∈ A \forall \langle t_1,t_2,t_3,\dots,t_k\rangle\in\N^k,\sum_{i=1}^{k}t_ip_i>m\vee \sum_{i=1}^{k}t_ip_i\in A ∀⟨t1,t2,t3,…,tk⟩∈Nk,∑i=1ktipi>m∨∑i=1ktipi∈A
- ∀ a ∈ A , ∃ ⟨ t 1 , t 2 , t 3 , … , t k ⟩ ∈ N k , ∑ i = 1 k t i p i = a \forall a\in A,\exist\langle t_1,t_2,t_3,\dots,t_k\rangle\in\N^k,\sum_{i=1}^{k}t_ip_i=a ∀a∈A,∃⟨t1,t2,t3,…,tk⟩∈Nk,∑i=1ktipi=a
用大白话说一遍:
- 从 P P P中选若干个元素(可以重复选择),对其求和,如果不大于 m m m,则这个和应当为 A A A中的元素。
- A A A中的任意一个元素都可以由上面的这种选择方式凑成。
这样的 P P P可能有很多个,你只需要输出集合大小,也就是 k k k,最小的一个。没有则输出“ NO \text{NO} NO”。
数据范围与约定
- n , m ≤ 1 0 6 n,m\le 10^6 n,m≤106
- A ⊆ Z , A ⊆ [ 1 , n ] A\subseteq \Z,A\subseteq[1,n] A⊆Z,A⊆[1,n]
思路
首先, p ≤ m p\le m p≤m;否则将其去掉,仍然合法。
结论一
显然,只在
P
P
P中选一个元素
p
x
p_x
px,也应当满足限制一
。根据题意有
p
x
>
m
∨
p
x
∈
A
p_x>m\vee p_x\in A
px>m∨px∈A。
而 p x ≤ m p_x\le m px≤m,所以只能是 p x ∈ A ⇔ P ⊆ A p_x\in A\Leftrightarrow P\subseteq A px∈A⇔P⊆A
然而,这并不是充要条件。在下文的论述中也应该注意这些关系,读者不妨自己考究一下,下面的这些结论也好、推论也罢,究竟是不是充要关系。
结论二
考虑 a x , a y ∈ A ( x ≠ y ) a_x,a_y\in A(x\ne y) ax,ay∈A(x=y)两个元素。
由限制二
,
a
x
,
a
y
a_x,a_y
ax,ay可以写成
p
p
p之和。故,
a
x
+
a
y
a_x+a_y
ax+ay可以写成
p
p
p之和。由限制一
,
p
p
p之和应当为
A
A
A的元素,否则大于
m
m
m。
总结一下, a x + a y ∈ A ∨ a x + a y > m a_x+a_y\in A\vee a_x+a_y>m ax+ay∈A∨ax+ay>m
用群论的话来说, ( A , + ) (A,+) (A,+)部分满足封闭性。
推论二
将一个累和的式子多用几次上面的结论二
,就会发现:
∑ { u } ⊆ A u ∈ A \sum_{\{u\}\subseteq A}u\in A {u}⊆A∑u∈A
由于 P ⊆ A P\subseteq A P⊆A,它还有一个附加产品:
∑ { u } ⊆ P u ∈ A \sum_{\{u\}\subseteq P}u\in A {u}⊆P∑u∈A
结论三
怎样让这个 P P P最小呢?
首先令
P
=
A
P=A
P=A,这样结论一
和限制二
都满足了。
如果去掉一些元素,结论一
显然仍然成立,所以只需要使限制二
仍成立。
考虑去掉
p
x
p_x
px的影响。不妨设
p
x
=
a
y
p_x=a_y
px=ay,那么原本限制二
中
a
y
a_y
ay可以由
p
x
p_x
px构成,那么现在便不可了。也就是说,
a
y
a_y
ay可以由
p
x
p_x
px以外的
p
p
p构成。
不妨设 a y = p 1 + p 2 + p 3 + ⋯ + p t a_y=p_1+p_2+p_3+\cdots+p_t ay=p1+p2+p3+⋯+pt(对于集合中的元素,顺序是无关紧要的,我完全可以将其重新排列)。
根据推论二
,
p
1
+
p
2
∈
A
,
p
3
+
p
4
∈
A
,
…
,
p
t
−
1
+
p
t
∈
A
p_1+p_2\in A,p_3+p_4\in A,\dots,p_{t-1}+p_t\in A
p1+p2∈A,p3+p4∈A,…,pt−1+pt∈A(即使
t
t
t为奇数,根据结论一
,
p
t
∈
A
p_t\in A
pt∈A)。
所以,
p x = a y = ∑ { u } ⊆ A u ⇔ ¬ ( p x ∈ P ) p_x=a_y=\sum_{\{u\}\subseteq A}u\Leftrightarrow \neg(p_x\in P) px=ay={u}⊆A∑u⇔¬(px∈P)
用文字来说,如果 p p p能够由 A A A中的元素表示,则 p p p可以不属于 A A A。
有意思的是,这是充要条件: a a a之和一定可以用 p p p之和来表示;不能由 a a a之和表示,也一定不能由 p p p之和表示。
推论三
还记得推论二
吗?
A
A
A中的元素之和还是某个
A
A
A中的元素。
将组成 p p p的这一堆 a a a分成两份,显然,两份都是某个 a a a.
于是乎,如果 p p p可以不属于 P P P,当且仅当 p = a x + a y p=a_x+a_y p=ax+ay
检查限制一
由于之前的结论一
并不是限制一
的充要条件,我们还需要再验证一次限制一
。
假设限制一
没有被满足,也就是
¬ ( ∑ { u } ⊆ P u ∈ A ) \neg\left(\sum_{\{u\}\subseteq P}u\in A\right) ¬⎝⎛{u}⊆P∑u∈A⎠⎞
发现这是很荒谬的:结论二
已经帮你解决了该问题。
总结
结论一
: P ⊆ A P\subseteq A P⊆A结论二
: a x + a y ∈ A ∨ a x + a y > m a_x+a_y\in A\vee a_x+a_y>m ax+ay∈A∨ax+ay>m推论三
:如果 p = a x + a y p=a_x+a_y p=ax+ay,那么 P P P可以去掉 p p p,否则不能
所以,算法就是下面的这个流程:
- 初始化集合 P = A P=A P=A
- 求出所有不超过 m m m的 a x + a y a_x+a_y ax+ay
- 对于其中一个和 r r r,如果 r ∈ A r\in A r∈A,则将其从 P P P中删去,否则输出 NO \text{NO} NO
- 检查完所有的和之后, P P P集合就是答案
那么怎么求 a x + a y a_x+a_y ax+ay呢?用一个生成函数即可:
f ( x ) = ∑ i = 0 m b i x i f(x)=\sum_{i=0}^{m}b_i x^i f(x)=i=0∑mbixi
b i = 1 b_i=1 bi=1当且仅当 i ∈ A i\in A i∈A,或者 i = 0 i=0 i=0。
f ( x ) f(x) f(x)与自己作卷积,那么 x k x^k xk的系数就是 ∑ i = 0 k b i b k − i \sum_{i=0}^{k}b_ib_{k-i} ∑i=0kbibk−i,将其记为 g ( k ) g(k) g(k)。
显然,这个 g ( k ) g(k) g(k)的意义就是选择 A ∪ { 0 } A\cup\{0\} A∪{0}中的两个元素(可以相同),有多少种不同的方案满足求和为 k k k。
只考虑 k ≠ 0 k\ne 0 k=0的情况。分一个类:
- 如果原本 k ∈ A k\in A k∈A:若 g ( k ) = 2 g(k)=2 g(k)=2,显然, 2 = b 0 b k + b k b 0 2=b_0b_k+b_kb_0 2=b0bk+bkb0——也就是说, k k k不能由两个 A A A中的元素相加而成——故 k ∈ P k\in P k∈P;若 g ( k ) ≠ 2 g(k)\ne 2 g(k)=2,则 k k k不属于 P P P。
- 如果 k k k不属于 A A A:若 g ( k ) > 0 g(k)>0 g(k)>0,显然,这意味着两个 A A A中的元素(可以重复)之和不属于 A A A。这意味着无解。反之,什么也不会发生。
卷积就很简单了,只需要做一次 FFT/NTT \text{FFT/NTT} FFT/NTT即可。反正我打的 NTT \text{NTT} NTT。
代码
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0' or c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c and c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(long long x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) writeint(x/10);
putchar(x%10+'0');
}
inline int qkpow(long long base,int q,int Mod){
long long ans = 1; base %= Mod;
for(; q; q>>=1,base=base*base%Mod)
if(q&1) ans = ans*base%Mod;
return ans;
}
const int MaxN = 4000005;
int omg[MaxN];
void NTT(int a[],int n,int Mod,int g,int opt){
omg[0] = 1;
for(int i=1; i<n; ++i)
omg[i] = 1ll*omg[i-1]*g%Mod;
int logN = 0;
while((1<<logN) < n) ++ logN;
for(int i=0; i<n; ++i){
int t = 0;
for(int j=0; (i>>j)!=0; ++j)
if(i>>j&1) t |= 1<<(logN-j-1);
if(t < i) swap(a[t],a[i]);
}
for(int len=2; len<=n; len<<=1)
for(int *p=a; p!=a+n; p+=len)
for(int i=0; i<(len>>1); ++i){
int t = 1ll*omg[(n/len*i*opt+n)%n]*p[i+(len>>1)]%Mod;
p[i+(len>>1)] = (p[i]-t+Mod)%Mod, p[i] = (p[i]+t)%Mod;
}
if(opt == -1){
int inv = qkpow(n,Mod-2,Mod);
for(int i=0; i<n; ++i)
a[i] = 1ll*a[i]*inv%Mod;
}
}
int n, m, f[MaxN], F[MaxN];
void input(){
n = readint(), m = readint();
for(int i=1; i<=n; ++i)
f[readint()] = 1;
f[0] = 1;
}
void solve(){
int N = 1;
while(N <= (m<<1)) N <<= 1;
const int Mod = 998244353;
const int g = qkpow(3,(Mod-1)/N,Mod);
for(int i=0; i<=m; ++i) F[i] = f[i];
NTT(f,N,Mod,g,1);
for(int i=0; i<N; ++i)
f[i] = 1ll*f[i]*f[i]%Mod;
NTT(f,N,Mod,g,-1);
bool ok = true;
for(int i=1; i<=m; ++i)
if(F[i] == 0 and f[i] >= 1)
ok = false;
if(not ok)
puts("NO");
else{
puts("YES");
int k = 0;
for(int i=1; i<=m; ++i)
if(F[i] == 1 and f[i] == 2)
++ k;
writeint(k), putchar('\n');
for(int i=1; i<=m; ++i)
if(F[i] == 1 and f[i] == 2)
writeint(i), putchar(' ');
putchar('\n');
}
}
int main(){
input(), solve();
return 0;
}