トピックポータル
まず$ a_iを> 0 $であることを検討してください。等しいか又はそれ以下$ W $より$ a_iを$いずれか後者の数の積は、$ W $よりも大きい。このような構成のシーケンスを考えます。
この設定を行うことができます。
ベクター<整数> B {0}。 ソート(a.begin()、a.end()); INTはL = 0、R =(符号付き)a.size() - 1; 一方、(L <= R){ IF(1LL * [L] * [R]> W){ b.push_back(b.back() - 1)。 r--の; }他{ b.push_back(b.back()+ 1)。 L ++; } } )(b.pop_back。
これは、注文の四種類を列挙する繰り返し試みが見つけることができ、ルートパーティションと考えることができます。
したがって、各$ a_iを$を列挙するために、我々はそれが位置の数に挿入することができることを知っています。のとその背後にある数の積は、それ以外の場合は$ W $、そして可能な位置マイナス1、プラス1よりも大きい場合。
この制限は、実行する方法を検討し、別々に正と負を検討し、セグメントの数を計算し、最終的にマージされません。
パーティションNTTの数で可能な最初のセグメントの数は約列挙は最初のセグメント多項式の数に実行可能な解決策を見つける、およびマルチポイントの評価は、その後、畳み込み二項反転、再びそれを行います。
時間複雑$ O(nは\ログ^ 2のn)$。
ここでコードを書くの質問をテストする時間があります。
コード
#include <ビット/ STDC ++ H> 名前空間STDを使用して、 typedef BOOLブール値。 #defineは長い長いっ テンプレート<typenameにT> {(PST、constのT * PED、TヴァルT *)無効pfill 用(; PST = PED;!*(PST ++)= val)で、 } テンプレート<型名T> ボイドPCOPY(T * PST、CONST T * PED、T * PVAL){ ため(;!PST = PED; *(PST ++)= *(PVAL ++))。 } CONST INT N = 262144。 const int型のMod = 998244353; const int型bzmax = 19; CONST INT G = 3。 ボイドexgcd(int型A、INT B、INT&X、INT&Y){ (B!の)場合、{ X = 1、Y = 0。 }他{ exgcd(B、%のB、Y、X)。 Y - =(/ B)* X。 } } INT INV(INT A、INT MOD){ int型のX、Y。 exgcd(MOD、X、Y) リターン(x <0の)?(X + MOD):(X)。 } テンプレート<CONST INTモッド= ::モジュレーション> クラスZ { パブリック: INT V。 Z():V(0){} Z(INT X):V(X){} Z(LL X):V(X%MOD){} フレンドZ演算子+(CONST Z&、CONST Z&B){ int型のx; リターンZ(((X = AV + BV)> = MOD)(X - MOD):(X)?)。 } フレンドZ演算子- (CONST Z&、CONST Z&B){ int型のX; 戻りZ(((X = AV - BV)<0)、(X + MOD):(X))。 } フレンドZ演算子*(CONST Z&、CONST Z&B){ 戻りZ(AV * 1LL * BV)。 } フレンドZ演算子〜(CONST Z&){ 戻りINV(AV、MOD)。 } フレンドZ演算子- (CONST Z&){ 戻りZ(0) - 。 } Z&演算子+ =(Z b)は{ リターン*この= *この+ B。 } Z&演算子- =(Z b)は{ リターン*この= *この- B。 } Z&演算子* =(Z b)は{ リターン*この= *本* B。 } 友人ブール演算子==(CONST Z&、CONST Z&B){ AV == BVを返します。 } }。 Zのtypedef <>紫。 ZI qpow(ZI、int型P){ IF(P <モッド- 1) P + =モッド- 1。 ZI RT = 1、PA =。 用(; P; P >> = 1、PA = PA * PA){ もし(P&1){ RT = RT * PA。 } } RT返します。 } CONST紫INV2((MOD + 1)>> 1)。 クラスNTT { プライベート: 紫GN [bzmax + 4]、_gn [bzmax + 4]。 公共: NTT(){ ため(INT I = 0; I <= bzmax; I ++){ GN [I] = qpow(ZI(G)、(MOD - 1)>> I)。 _gn [I] = qpow(ZI(G)、 - ((MOD - 1)>> I))。 } } ボイド演算子()(ZI * F、INT LEN、INT SGN){ (INTはiが1、J = LEN >> 1、K =; I <LEN - 1; I ++、J + = k)に対する{ 場合を( I <J) スワップ(F [I]、F [J])。 用(K = LEN >> 1; K <= J; J - = K、K >> = 1)。 } 紫* WN =(SGN> 0)?(GN + 1):(_gn + 1)、A、B、W。 のための(int型、L = 2、HL; L <= LEN; L << = 1、WN ++){ HL = L >> 1、 以下のために(INT iは= 0; I <LEN、I + = L、1 = W){ ため(INT J = 0; J <HL; J ++、W * = * WN){ = F [I + J] B = F [I + J + HL] * W。 F [I + J] = A + B。 F [I + J + HL] = - B。 } } } もし(SGN <0){ 紫invlen =〜紫(LEN)。 以下のために(INT I 0 =; I <LEN; I ++){ F [I] * = invlen。 } } } int型correct_len(INT LEN){ int型、M = 1。 用(; M <= LEN; M << = 1)。 メートルを返します。 } } NTT。 ボイドpol_inverse(ZI * F、紫* G、int型N){ 静的紫A [N]。 IF(N == 1){ G [0] =〜F [0]。 }他{ int型HN =(N + 1)>> 1、T = NTT.correct_len(N << 1 | 1)。 pol_inverse(F、G、HN)。 PCOPY(A、A + N、F)。 pfill(A + N、A + tの、紫(0))。 pfill(G + HN、G + T、紫(0))。 NTT(A、T、1); NTT(G、T、1); 以下のために(INT I 0 =; I <T、I ++){ G [I] = G [I] *(ZI(2) - G [I] * A [I])。 } NTT(G、T、-1)。 pfill(G + N、G + T、紫(0))。 } } ボイドpol_sqrt(ZI * F、紫* G、int型N){ 静的紫A [N]、B [N]。 IF(N == 1){ G [0] = F [0]。 }他{ int型HN =(N + 1)>> 1、T = NTT.correct_len(N + N)。 pol_sqrt(F、G、HN)。 pfill(G + HN、G + nは、紫(0))。 以下のために(INT iが= 0; I <HN; I ++) A [I] = G [I] + G [i]は、 pfill(A + HN、A + T、紫(0))。 pol_inverse(A、B、N)。 PCOPY(A、A + N、F)。 pfill(A + N、A + tの、紫(0))。 NTT(A、T、1); NTT(B、T、1); 以下のために(INT iが= 0; I <T、I ++) A [i]が* = B [i]は、 NTT(A、T、-1)。 (; I <N I ++ INTがI = 0)するための G [I] = G [I] * + A [i]のINV2。 } } クラスポリのtypedef:公共ベクトル<紫> { パブリック: 使用してベクトル<紫> ::ベクター。 ポリ・フィックス(int型SZ){ リサイズ(SZ)。 *これを返します。 } }ポリ。 ポリオペレータ+(ポリA、ポリB){ int型N = A.size()、M = B.size()。 INT、T = MAX(N、M); A.resize(T)、B.resize(T)。 以下のために(INT I 0 =; I <T、I ++){ A [I] + = B [i]は、 } Aを返します。 } ポリ演算子- (ポリA、ポリB){ int型N = A.size()、M = B.size()。 INT、T = MAX(N、M); A.resize(T)、B.resize(T)。 以下のために(INT I 0 =; I <T、I ++){ A [1] - = B [i]は、 } Aを返します。 } ポリSQRT(ポリA){ ポリRT(a.size())。 pol_sqrt(a.data()、rt.data()、a.size())。 RT返します。 } ポリ演算子*(ポリA、ポリB){ int型N = A.size()、M = B.size()。 INT K = NTT.correct_len(N + M - 1)。 (20 <N || M <20)であれば{ ポリRT(N + M - 1)。 以下のために(INT I = 0、I <N; I ++){ int型N = A.size()、M = B.size()。 ため(INT J = 0であり、j <M、J ++){ RT [I + J] + = A [I] * B [J]。 } } RT返します。 } A.resize(K)、B.resize(K)。 NTT(A.data()、K、1)。 NTT(B.data()、K、1)。 以下のために(INT I 0 =; I <K; I ++){ A [I] * = B [i]は、 } NTT(A.data()、K、-1); A.resize(N + M - 1)。 Aを返します。 } ポリ演算子〜(ポリF){ int型N = f.size()、T = NTT.correct_len((N << 1)| 1)。 ポリRT(T); f.resize(T)。 pol_inverse(f.data()、rt.data()、N)。 rt.resize(N) RT返します。 } ポリオペレータ/(ポリA、ポリB){ IF(N <M){ リターンポリ{0}。 } INT R = N - M + 1。 (A.begin()、A.end())逆。 逆(B.begin()、B.end())。 A.resize(R)、B.resize(R) A = A *〜B; A.resize(R) (A.begin()、A.end())逆。 Aを返します。 } ポリ演算子%(ポリA、ポリB){ int型N = A.size()、M = B.size()。 IF(N <M){ 戻りA。 } (M == 1){場合 、ポリ返す{0}。 } A = A - A / B * B。 A.resize(M - 1)。 Aを返します。 } 紫Invの[N]。 ボイドinit_inv(int型N){ Invの[0] = 0、Invの[1] = 1。 以下のために(INT I 2 =; I <= N; I ++){ INV [I] = INV [モッド%I] *紫((MOD - (MOD / I)))。 } } ボイド差分(ポリ&F){ もし(f.size()== 1){ F [0] = 0; リターン; } ため(INT I 1 =; I <(符号付き)f.size(); I ++){ F [I - 1] = F [I] *紫(I)。 } f.resize(f.size() - 1)。 } ボイドINTEG(ポリ&F){ f.resize(f.size()+ 1)。 用(INT I =(符号付き)f.size() - 1; iは; i--){ F [I] = F [I - 1] * Invの[I]。 } F [0] = 0; } ポリLN(ポリF){ int型N = f.size()。 ポリH = F。 差分(H)。 F = H *〜F; f.resize(N - 1)。 INTEG(F)。 Fを返します。 } ボイドpol_exp(ポリ&F、ポリ&G、INT N){ ポリH。 (N == 1)であれば{ g.resize(1)。 G [0] = 1; }他{ int型HN =(N + 1)>> 1。 pol_exp(F、G、HN)。 h.resize(N)、g.resize(N) PCOPY(h.data()、h.data()+ N、f.data())。 G = G *(ポリ{1} - LN(G)+ H)。 g.resize(N) } } ポリEXP(ポリF){ int型N = f.size()。 ポリRT; pol_exp(F、RT、N)。 RT返します。 } クラスPolyBuilder { 保護: INT NUM。 ポリP [N << 1]。 ボイド_init(INT * X、int型のL、R INT){ IF(L == R){ P [NUM ++] =ポリ{-Zi(X [L])、紫(1)}。 リターン; } INTミッド=(L + R)>> 1。 int型curid = NUM ++; _init(X、L、MID)。 INT = numは取り除きます。 _init(X、中間+ 1、R)。 P [curid] = P [curid + 1] * P [RID]。 } ボイド_evalute(ポリF、紫* Y、int型のL、int型R){ F = F%P [NUM ++]。 IF(L == R){ Y [L] = F [0]。 リターン; } int型のミッド=(L + R)>> 1。 _evalute(F、Y、L、MID)。 _evalute(F、Y、中間+ 1、R)。 } 公共: ポリevalute(ポリF、INT * X、INT N){ ポリRT(N) NUM = 0; _init(X、0、N - 1)。 NUM = 0; _evalute(F、rt.data()、0、N - 1)。 RT返します。 } } PolyBuilder。 ostream&演算子<<(のostream&OS、ポリ&F){ ための(自動X:F) OS << XV << '「。 OS << '\ n'は、 OSを返します。 } 紫FAC [N]、_fac [N]。 ボイドinit_fac(int型N){ FAC [0] = 1; 以下のために(; <I = N;整数iが1 = I ++){ FAC [i]は= FAC [I - 1] * I。 } _fac [N] =〜FAC [N]。 用(INT I = N; I; i--){ _fac [I - 1] = _fac [I] * I。 } } int型、W。 ポリ分割(INT *、INT L、INT R){ IF(L == R) ポリ{[L]、1}を返します。 INTミッド=(L + R)>> 1。 リターン分割(L、MID)*分割(中間+ 1、R)。 } int型XS [N]。 ポリ作業(ベクトル<整数> A、INT maxseg){ IF(!a.size()){ ポリRT(maxseg、紫(0))。 RT [0] = 1; RT返します。 } (オート・X:)のための (X <0)&&(X = -x)。 ベクター<整数> B {0}。 ソート(a.begin()、a.end()); INTはL = 0、R =(符号付き)a.size() - 1; 一方、(L <= R){ IF(1LL * [L] * [R]> W){ b.push_back(b.back() - 1)。 r--の; }他{ b.push_back(b.back()+ 1)。 L ++; } } )(b.pop_back。 (オート・X:B)のための (X <0)&&(X + = MOD)。 ポリF =(b.data()、0、(符号付き)b.size() - 1)を分割します。 F = PolyBuilder.evalute(F、XS、maxseg)。 以下のために(INT I 0 =; I <(符号付き)f.size(); I ++){ F [I] * = _fac [I]。 } 、ポリG(f.size())。 以下のために(INT I 0 =; I <(符号付き)g.size(); I ++){ G [I] = _fac [I]。 (I&1)&&(G [I] = -g [I]、0); } F =(F * G).fix(maxseg)。 以下のために(INT I 0 =; I <(符号付き)f.size(); I ++) F [I] * = FAC [i]は、 Fを返します。 } INT N; [N]をint型。 INTメイン(){ scanf関数( "%D%D"、&N、&W) ベクター<整数> A、B。 用(INT iは= 1; I <= N; I ++){ scanf関数( "%のD"、+ I)。 IF([I] <0){ A.push_back([I])。 } } init_fac(N + 3)。 INT maxseg =分(A.size()、B.size())+ 3。 以下のために(INT I = 1; I <maxseg; I ++) XS [I] = I。 ポリG =仕事(B、maxseg)。 ZI ANS = 0。 以下のために(INT I = 0; I <maxseg; I ++){ ANS + = F [I] * G [I] * 2。 IF(I) ANS + = F [I] * G [I - 1]。 IF(I <maxseg - 1) ANS + = F [I] * G [I + 1]。 } のprintf( "%D \ n"、ans.v)。 0を返します。 }