//Timestamp: 2024-08-11 15:12:19 #define DROP #ifdef ONLINE #undef LOCAL #endif #ifndef LOCAL #undef _GLIBCXX_DEBUG #undef _DEBUG #endif #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include "compiler_hint.cpp" template struct MDVecDef { using Type = std::vector::Type>; template static Type Make(int n, Args... args) { return Type(n, MDVecDef::Make(args...)); } }; template struct MDVecDef { using Type = T; static Type Make(T val = T()) { return val; } }; template using MDVec = typename MDVecDef::Type; #ifndef M_PI #define M_PI 3.14159265358979323851280895940618620443274267017841L #endif #ifndef M_E #define M_E 2.718281828459045235428168107993940338928950950503355L #endif #ifdef LOCAL #define Assert(x) assert(x) #define DebugRun(X) X #define DebugPoint int _x_ = 0; _x_++; #else #define Debug(...) 42 #define DebugFmtln(...) 42 #define Assert(x) 42 #define DebugRun(X) #define DebugPoint #endif #define Trace(x) DebugFmtln("Line %d: %s", __LINE__, #x) template inline T DebugRet(T x) { Debug(x); return x; } #define const_ref(T) const T & #define mut_ref(T) T & #define let auto #define var auto #define MEMSET0(X) std::memset(&X, 0, sizeof(X)) #define Size(T) int((T).size()) #define All(data) data.begin(), data.end() #define MakeUnique(data) data.resize(std::unique(All(data)) - data.begin()) #define MakeUniqueAndSort(data) Sort(All(data)); MakeUnique(data) #define MakeAttribute(struct_name, Type, attr_name) \ struct struct_name { \ using attr_name ## _type = Type; \ Type attr_name; \ mut_ref(Type) get_##attr_name() { return attr_name; } \ const_ref(Type) get_##attr_name() const { return attr_name; } \ }; #define MakeTemplateAttribute(struct_name, attr_name) \ template \ struct struct_name { \ using attr_name##_type = T; \ T attr_name; \ mut_ref(T) get_##attr_name() { return attr_name; } \ const_ref(T) get_##attr_name() const { return attr_name; } \ }; #define ImplDefaultEq(name) \ bool operator==(const name &a, const name &b) { \ return std::memcmp(&a, &b, sizeof(name)) == 0; \ } \ bool operator!=(const name &a, const name &b) { return !(a == b); } #define ImplDefaultComparision(name) \ bool operator>(const name &rhs) const { return rhs < *this; } \ bool operator<=(const name &rhs) const { return !(*this > rhs); } \ bool operator>=(const name &rhs) const { return !(*this < rhs); } #define ImplArithmeticAssignOperation(name) \ name &operator+=(const name &rhs) { return *this = (*this) + rhs; } \ name &operator-=(const name &rhs) { return *this = (*this) - rhs; } \ name &operator*=(const name &rhs) { return *this = (*this) * rhs; } \ name &operator/=(const name &rhs) { return *this = (*this) / rhs; } #define IsType(Type, param, ret_type) \ template \ enable_if_t && is_same_v, \ ret_type> #define IsBool(param, ret_type) \ template \ enable_if_t #define IsBoolStatic(param, ret_type) \ template \ static enable_if_t #define MakeAnnotation(name) \ template \ struct is_##name { \ static const bool value = false; \ }; \ template \ inline constexpr bool is_##name##_v = is_##name::value; #define AssignAnnotation(cls, annotation) \ template <> \ struct is_##annotation { \ static const bool value = true; \ }; #define AssignAnnotationTemplate(cls, annotation, type) \ template \ struct is_##annotation> { \ static const bool value = true; \ }; #define FunctionAlias(from, to) \ template \ inline auto to(Args &&...args) \ ->decltype(from(std::forward(args)...)) { \ return from(std::forward(args)...); \ } #define CastToScalar(field, type) \ operator type() const { return type(field); } #define CastToAllScalar(field) \ CastToScalar(field, i8); \ CastToScalar(field, u8); \ CastToScalar(field, i16); \ CastToScalar(field, u16); \ CastToScalar(field, i32); \ CastToScalar(field, u32); \ CastToScalar(field, i64); \ CastToScalar(field, u64); \ CastToScalar(field, f32); \ CastToScalar(field, f64); \ CastToScalar(field, f80); #define COMMA , #ifndef LOCAL std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count()); #else std::mt19937 rng(0); #endif template T random_choice(T l, T r, std::mt19937 &gen = rng) { std::uniform_int_distribution random(l, r); return random(gen); } namespace dalt { #ifndef LOCAL struct Timer {explicit Timer(const char* m) {}void stop() const {}}; #else #endif } using i8 = char; using i16 = short; using i32 = int; using i64 = long long; using u8 = unsigned char; using u16 = unsigned short; using u32 = unsigned int; using u64 = unsigned long long; using usize = size_t; using f32 = float; using f64 = double; // 16 exp, 64 precision using f80 = long double; FunctionAlias(std::lower_bound, LowerBound); FunctionAlias(std::upper_bound, UpperBound); FunctionAlias(std::unique, Unique); FunctionAlias(std::swap, Swap); FunctionAlias(std::min, Min); FunctionAlias(std::max, Max); FunctionAlias(std::abs, Abs); FunctionAlias(std::sin, Sin); FunctionAlias(std::asin, Asin); FunctionAlias(std::cos, Cos); FunctionAlias(std::acos, Acos); FunctionAlias(std::tan, Tan); FunctionAlias(std::atan, Atan); FunctionAlias(std::sort, Sort); FunctionAlias(std::fill, Fill); FunctionAlias(std::move, Move); FunctionAlias(std::reverse, Reverse); FunctionAlias(std::max_element, MaxElement); FunctionAlias(std::min_element, MinElement); FunctionAlias(std::make_tuple, MakeTuple); FunctionAlias(std::make_pair, MakePair); FunctionAlias(std::clamp, Clamp); FunctionAlias(std::shuffle, Shuffle); FunctionAlias(std::to_string, ToString); FunctionAlias(std::tie, Tie); FunctionAlias(std::get<0>, Get0); FunctionAlias(std::get<1>, Get1); FunctionAlias(std::get<2>, Get2); FunctionAlias(std::get<3>, Get3); FunctionAlias(std::get<4>, Get4); template using Function = std::function<_Signature>; template using Func = Function<_Signature>; using Str = std::string; using String = Str; using StringStream = std::stringstream; using IStream = std::istream; using OStream = std::ostream; using std::enable_if; using std::enable_if_t; using std::is_base_of; using std::is_base_of_v; using std::is_floating_point; using std::is_floating_point_v; using std::is_integral; using std::is_integral_v; using std::is_arithmetic; using std::is_arithmetic_v; using std::is_same; using std::is_same_v; using std::tie; auto &Stderr = std::cerr; auto &Stdin = std::cin; auto &Stdout = std::cout; template using Less = std::less; template using Greater = std::greater; template > using TreeMap = std::map<_Key, _Tp, _Compare>; template > using TreeSet = std::set<_Key, _Compare>; template , typename _Alloc = std::allocator<_Key>> using MultiTreeSet = std::multiset<_Key, _Compare, _Alloc>; template using Deque = std::deque; template using Queue = std::queue; template using Vec = std::vector; template using Reducer = Func; template using Comparator = Func; template using Indexer = Func; template using Indexer2 = Func; template using Adder = Func; template using Checker = Func; template using BiChecker = Func; template using Consumer = Func; template using Supplier = Func; template using BiConsumer = Func; template using Mapper = Func; template using MinHeap = std::priority_queue, Greater>; template using MaxHeap = std::priority_queue, Less>; template using Array = std::array; template using Tuple = std::tuple<_Elements...>; template >> using Complex = std::complex; template using Pair = std::pair; namespace dalt { template IStream& operator>>(IStream& is, Vec& val) { for (auto& v : val) { is >> v; } return is; } #define VEC_OP(op) \ template \ Vec& operator op(Vec& data, T x) { \ for (auto& v : data) { \ v op x; \ } \ return data; \ } VEC_OP(+=) VEC_OP(-=) VEC_OP(*=) VEC_OP(/=) VEC_OP(%=) VEC_OP(^=) VEC_OP(&=) VEC_OP(|=) VEC_OP(==) VEC_OP(!=) template int Compare(const Vec& lhs, const Vec& rhs) { for(int i = 0; i < Size(lhs) && i < Size(rhs); i++) { if(lhs[i] != rhs[i]) { return lhs[i] < rhs[i] ? -1 : 1; } } return Size(lhs) < Size(rhs) ? -1 : Size(lhs) > Size(rhs) ? 1 : 0; } template bool operator<(const Vec& lhs, const Vec& rhs) { return Compare(lhs, rhs) < 0; } template bool operator>(const Vec& lhs, const Vec& rhs) { return Compare(lhs, rhs) > 0; } template bool operator<=(const Vec& lhs, const Vec& rhs) { return Compare(lhs, rhs) <= 0; } template bool operator>=(const Vec& lhs, const Vec& rhs) { return Compare(lhs, rhs) >= 0; } } // namespace dalt //#include "array_adder.cpp" using namespace dalt; namespace dalt { template struct Optional { using Self = Optional; private: T val; bool show_up; public: Optional(const T &arg_val) : val(arg_val), show_up(true) {} Optional(const T &&arg_val) : val(arg_val), show_up(true) {} Optional() : show_up(false) {} const T &value() const { Assert(show_up); return val; } T &value() { Assert(show_up); return val; } T &operator*() { return value(); } const T &operator*() const { return value(); } bool is_some() const { return show_up; } bool is_none() const { return !show_up; } const T *operator->() const { return &value(); } T *operator->() { return &value(); } inline operator T() const { return value(); } T or_else(T def) const { if (is_some()) { return val; } else { return def; } } template Optional map(const Mapper &mapper) const { if (is_some()) { return mapper(value()); } else { return Optional(); } } bool operator==(const Self &b) const { return show_up == b.show_up && (!show_up || val == b.val); } }; template bool operator!=(const Optional &a, const Optional &b) { return !(a == b); } template OStream &operator<<(OStream &os, const Optional &v) { if (v.is_none()) { os << "{}"; } else { os << '{' << v.value() << '}'; } return os; } } // namespace dalt namespace dalt { template inline enable_if_t, T> Gcd(T a, T b) { while (b != 0) { a %= b; Swap(a, b); } return a; } // ret_value = [x, y, gcd(a,b)] that x * a + y * b = gcd(a, b) template inline enable_if_t, Array> ExtGcd(T a, T b) { if (b == 0) { return Array{1, 0, a}; } auto div = a / b; auto ans = ExtGcd(b, a - b * div); auto x = ans[0]; auto y = ans[1]; return Array{y, x - a / b * y, ans[2]}; } template inline enable_if_t, Optional> PossibleModInverse( T a, T modulus) { auto res = ExtGcd(a, modulus); if (res[2] == 1) { auto ans = res[0] % modulus; if (ans < 0) { ans += modulus; } return ans; } return {}; } } // namespace dalt namespace dalt { template T Modular(E val, T mod) { val = val % mod; if (val < 0) { val = val + mod; } return T(val); } template inline T MulMod(T a, T b, T modulus) { i64 res = i64(a) * i64(b) % modulus; return T(res); } template<> inline i64 MulMod(i64 a, i64 b, i64 modulus) { #ifdef __SIZEOF_INT128__ // do some fancy stuff here return __int128_t(a) * __int128_t(b) % modulus; #else // do some fallback stuff here i64 k = roundl((f80)a / modulus * b); i64 res = (a * b - k * modulus) % modulus; if (res < 0) { res += modulus; } return res; #endif } template inline enable_if_t, T> AddMod(T a, T b, T modulus) { T res = a + b; if (res >= modulus) { res -= modulus; } return res; } template inline enable_if_t && is_integral_v, T> PowMod(T x, E exp, T modulus) { Assert(exp >= E(0)); if (exp == E(0)) { return modulus == T(1) ? T(0) : T(1); } T ans = PowMod(x, exp >> 1, modulus); ans = MulMod(ans, ans, modulus); if (exp & T(1)) { ans = MulMod(ans, x, modulus); } return ans; } template inline enable_if_t, T> SubMod(T a, T b, T modulus) { T res = a - b; if (res < T(0)) { res += modulus; } return res; } } // namespace dalt namespace dalt { template inline A& Chmin(A& a, const B& b) { if (a > b) { a = b; } return a; } template inline T& Chmin(T& a, const T& b, const Comparator &comp) { if (comp(b, a)) { a = b; } return a; } template inline A& Chmax(A& a, const B& b) { if (a < b) { a = b; } return a; } template inline T& Chmax(T& a, const T& b, const Comparator& comp) { if (comp(a, b)) { a = b; } return a; } template inline T& AddTo(T& a, const T& b) { a = a + b; return a; } template inline T& MulTo(T& a, const T& b) { a = a * b; return a; } template inline T& SubFrom(T& a, const T& b) { a = a - b; return a; } template inline T& DivFrom(T& a, const T& b) { a = a / b; return a; } template constexpr enable_if_t, T> PowBinaryLift(T x, E n) { if (n == E(0)) { return T(1); } auto ans = PowBinaryLift(x, n >> 1); ans = ans * ans; if (n & 1) { ans = ans * x; } return ans; } template inline T MulLimit(T a, T b, T max, T def) { if (a == T(0) || b == T(0)) { return T(0); } // a * b <= max // a <= max / b // a <= floor(max / b) if (a <= max / b) { return a * b; } else { return def; } } template inline T MulLimit(T a, T b, T max) { return MulLimit(a, b, max, max); } template inline T AddLimit(T a, T b, T max, T def) { if (a <= max - b) { return a + b; } else { return def; } } template inline T AddLimit(T a, T b, T max) { return AddLimit(a, b, max, max); } i64 Round(f32 x) { return roundf(x); } i64 Round(f64 x) { return round(x); } i64 Round(f80 x) { return roundl(x); } //l + ... + r template T SumOfInterval(T l, T r) { if(l > r) { return T(0); } return (l + r) * (r - l + 1) / T(2); } template T Pow2(T x) { return x * x; } } // namespace dalt namespace dalt { // using Type = T; // static T modulus; // static T primitive_root; MakeAnnotation(modular); template struct StaticModular { static_assert(T(M + M) >= 0); static_assert(is_integral_v); const static T modulus = M; const static T primitive_root = PR; const static T phi = PHI; using Type = T; }; template struct is_modular> { static const bool value = true; }; using MOD998244353 = StaticModular; using MOD1004535809 = StaticModular; using MOD1000000007 = StaticModular; using MOD1000000009 = StaticModular; using MOD_BIG = StaticModular; // last used: -2 template struct DynamicModular { static_assert(is_integral_v); static T modulus; static T primitive_root; static T phi; static void Register(T _modulus, T _primitive_root = T(), T _phi = T()) { modulus = _modulus; primitive_root = _primitive_root; phi = _phi; } using Type = T; }; template T DynamicModular::modulus = T(); template T DynamicModular::primitive_root = T(); template T DynamicModular::phi = T(); template struct is_modular> { static const bool value = true; }; MakeAnnotation(modint); MakeAnnotation(modint_32); #define MOD MODULAR::modulus #define SELF ModInt #define TEMPLATE_ARGS template TEMPLATE_ARGS struct ModInt { using Modular = MODULAR; using Type = typename MODULAR::Type; static_assert(is_modular_v); static_assert(is_integral_v); Type value; using Self = SELF; ModInt() : value(0) {} ModInt(const Type &v) { value = v; if (value < 0 || value >= MOD) { value %= MOD; if (value < 0) { value += MOD; } } Assert(value >= 0); } static Self nil() { Self res; res.value = -1; return res; } bool is_nil() { return value == -1; } explicit operator Type() const { return value; } static Type modulus() { return MOD; } static Type primitive_root() { return Modular::primitive_root; } Self operator-() const { return Self(0) - *this; } template static enable_if_t, Self> of(F v) { v %= MOD; if (v < 0) { v += MOD; } return Self(v); } Optional possible_inv() const { auto raw_optional_inv = PossibleModInverse(value, MOD); if (raw_optional_inv.is_some()) { return Self(raw_optional_inv.value()); } else { return {}; } } Self operator+(const Self &rhs) const { auto res = value + rhs.value; if (res >= MOD) { res -= MOD; } return res; } Self operator-(const Self &rhs) const { auto res = value - rhs.value; if (res < Type(0)) { res += MOD; } return res; } Self operator/(const SELF &rhs) const { auto inv = Self(rhs.possible_inv().value()); return (*this) * inv; } bool operator==(const Self &b) const { return value == b.value; } bool operator!=(const Self &b) const { return value != b.value; } bool operator<(const Self &b) const { return value < b.value; } ImplDefaultComparision(Self); ImplArithmeticAssignOperation(Self); template enable_if_t, Self> pow(E n) { return PowBinaryLift(*this, n); } friend inline IStream &operator>>(IStream &is, Self &x) { Type val; is >> val; x = Self::of(val); return is; } friend inline OStream &operator<<(OStream &os, const Self &x) { os << x.value; return os; } }; TEMPLATE_ARGS inline enable_if_t, SELF> operator*( const SELF &a, const SELF &b) { return SELF(MulMod(a.value, b.value, MOD)); } TEMPLATE_ARGS inline enable_if_t, SELF> operator*( const SELF &x, const SELF &y) { static u64 mask = (u64(1) << 32) - 1; static u64 mod = MOD_BIG::modulus; u64 a = x.value; u64 b = y.value; u64 l1 = a & mask; u64 h1 = (a >> 32) & mask; u64 l2 = b & mask; u64 h2 = (b >> 32) & mask; u64 l = l1 * l2; u64 m = l1 * h2 + l2 * h1; u64 h = h1 * h2; u64 ret = (l & mod) + (l >> 61) + (h << 3) + (m >> 29) + ((m << 35) >> 3) + 1; ret = (ret & mod) + (ret >> 61); ret = (ret & mod) + (ret >> 61); return SELF(ret - 1); } TEMPLATE_ARGS struct is_modint> { static const bool value = true; }; TEMPLATE_ARGS struct is_modint_32> { static const bool value = is_same_v; }; #undef TEMPLATE_TYPE #undef MOD using ModInt998244353 = ModInt; using ModInt1000000007 = ModInt; using ModInt1000000009 = ModInt; using ModInt1004535809 = ModInt; } // namespace dalt using namespace std; template struct LazyMontgomeryModInt { using mint = LazyMontgomeryModInt; using i32 = int32_t; using u32 = uint32_t; using u64 = uint64_t; static constexpr u32 get_r() { u32 ret = mod; for (i32 i = 0; i < 4; ++i) ret *= 2 - mod * ret; return ret; } static constexpr u32 r = get_r(); static constexpr u32 n2 = -u64(mod) % mod; static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30"); static_assert((mod & 1) == 1, "invalid, mod % 2 == 0"); static_assert(r * mod == 1, "this code has bugs."); u32 a; constexpr LazyMontgomeryModInt() : a(0) {} constexpr LazyMontgomeryModInt(const int64_t &b) : a(reduce(u64(b % mod + mod) * n2)){}; static constexpr u32 reduce(const u64 &b) { return (b + u64(u32(b) * u32(-r)) * mod) >> 32; } constexpr mint &operator+=(const mint &b) { if (i32(a += b.a - 2 * mod) < 0) a += 2 * mod; return *this; } constexpr mint &operator-=(const mint &b) { if (i32(a -= b.a) < 0) a += 2 * mod; return *this; } constexpr mint &operator*=(const mint &b) { a = reduce(u64(a) * b.a); return *this; } constexpr mint &operator/=(const mint &b) { *this *= b.inverse(); return *this; } constexpr mint operator+(const mint &b) const { return mint(*this) += b; } constexpr mint operator-(const mint &b) const { return mint(*this) -= b; } constexpr mint operator*(const mint &b) const { return mint(*this) *= b; } constexpr mint operator/(const mint &b) const { return mint(*this) /= b; } constexpr bool operator==(const mint &b) const { return (a >= mod ? a - mod : a) == (b.a >= mod ? b.a - mod : b.a); } constexpr bool operator!=(const mint &b) const { return (a >= mod ? a - mod : a) != (b.a >= mod ? b.a - mod : b.a); } constexpr mint operator-() const { return mint() - mint(*this); } constexpr mint operator+() const { return mint(*this); } constexpr mint pow(u64 n) const { mint ret(1), mul(*this); while (n > 0) { if (n & 1) ret *= mul; mul *= mul; n >>= 1; } return ret; } constexpr mint inverse() const { int x = get(), y = mod, u = 1, v = 0, t = 0, tmp = 0; while (y > 0) { t = x / y; x -= t * y, u -= t * v; tmp = x, x = y, y = tmp; tmp = u, u = v, v = tmp; } return mint{u}; } friend ostream &operator<<(ostream &os, const mint &b) { return os << b.get(); } friend istream &operator>>(istream &is, mint &b) { int64_t t; is >> t; b = LazyMontgomeryModInt(t); return (is); } constexpr u32 get() const { u32 ret = reduce(a); return ret >= mod ? ret - mod : ret; } static constexpr u32 get_mod() { return mod; } }; using MOD = StaticModular; using Mi = ModInt; //using Mi = LazyMontgomeryModInt<1234567891>; void SolveOne(int test_id, IStream &in, OStream &out) { i32 N; i64 M; in >> N >> M; Vec A(N); in >> A; int sum = 0; for(var a : A) { sum += a; } Debug(N); Debug(M); Debug(A); var create_dp = [&]() { return MDVecDef::Make(2 * sum + 1, Mi(0)); }; var dp = create_dp(); var print_dp = [&]() { DebugRun({ for(int i = 0; i < Size(dp); i++) { DebugFmtln("dp[%d] = %d", i, dp[i]); } }) }; dp[0] = 1; //Debug(dp); for(int bit = 0; (i64(1) << bit) <= M; bit++) { //Debug(bit); for(var a : A) { var next = create_dp(); for(int i = 0; i < Size(next); i++) { if(i + a < Size(next)) { next[i + a] += dp[i]; } next[i] += dp[i]; } dp = Move(next); // Debug(a); // Debug(dp); } //compact int current_bit = (M >> bit) & 1; var next = create_dp(); for(int i = current_bit; i < Size(dp); i += 2) { next[i / 2] = dp[i]; } dp = Move(next); //Debug(dp); } var ans = dp[0]; out << ans; } void SolveMulti(IStream &in, OStream &out) { //std::ifstream input("in"); int num_of_input = 1; //in >> num_of_input; for (int i = 0; i < num_of_input; i++) { //SolveOne(i + 1, input, out); SolveOne(i + 1, in, out); } } int main() { std::ios_base::sync_with_stdio(false); Stdin.tie(0); Stdout << std::setiosflags(std::ios::fixed); Stdout << std::setprecision(15); #ifdef STRESS stress::Stress(); #else SolveMulti(Stdin, Stdout); #endif return 0; }