题目传送门

  传送门

  先考虑 $a_i > 0$ 的情况。考虑构造这样一个顺序:$a_i$ 要么和后面的数的乘积都大于 $w$ 要么都小于等于 $w$。

  这个构造可以这样做:

  1. vector<int> b {0};
  2. sort(a.begin(), a.end());
  3. int l = 0, r = (signed) a.size() - 1;
  4. while (l <= r) {
  5. if (1ll * a[l] * a[r] > w) {
  6. b.push_back(b.back() - 1);
  7. r--;
  8. } else {
  9. b.push_back(b.back() + 1);
  10. l++;
  11. }
  12. }
  13. b.pop_back();

  这个可以考虑根号分治,反复尝试 4 种枚举顺序可以发现。

  那么按顺序枚举每个 $a_i$,我们知道它可以插入的位置的数量。如果它和后面的数的乘积都大于 $w$,那么可行位置减 1,否则加 1.

  考虑没有这个限制条件怎么做,考虑正负分开,计算一下段数,最后再合并。

  枚举一下初始可行段数量,用分治 NTT 求出方案数关于初始可行段数量多项式,然后多点求值,然后一遍卷积做一下二项式反演。

  时间复杂度 $O(n\log^2 n)$。

  下面是验题的时候写的代码。

Code

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef bool boolean;
  4.  
  5. #define ll long long
  6.  
  7. template <typename T>
  8. void pfill(T* pst, const T* ped, T val) {
  9. for ( ; pst != ped; *(pst++) = val);
  10. }
  11.  
  12. template <typename T>
  13. void pcopy(T* pst, const T* ped, T* pval) {
  14. for ( ; pst != ped; *(pst++) = *(pval++));
  15. }
  16.  
  17. const int N = 262144;
  18. const int Mod = 998244353;
  19. const int bzmax = 19;
  20. const int g = 3;
  21.  
  22. void exgcd(int a, int b, int& x, int& y) {
  23. if (!b) {
  24. x = 1, y = 0;
  25. } else {
  26. exgcd(b, a % b, y, x);
  27. y -= (a / b) * x;
  28. }
  29. }
  30.  
  31. int inv(int a, int Mod) {
  32. int x, y;
  33. exgcd(a, Mod, x, y);
  34. return (x < 0) ? (x + Mod) : (x);
  35. }
  36.  
  37. template <const int Mod = :: Mod>
  38. class Z {
  39. public:
  40. int v;
  41.  
  42. Z() : v(0) { }
  43. Z(int x) : v(x){ }
  44. Z(ll x) : v(x % Mod) { }
  45.  
  46. friend Z operator + (const Z& a, const Z& b) {
  47. int x;
  48. return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
  49. }
  50. friend Z operator - (const Z& a, const Z& b) {
  51. int x;
  52. return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
  53. }
  54. friend Z operator * (const Z& a, const Z& b) {
  55. return Z(a.v * 1ll * b.v);
  56. }
  57. friend Z operator ~ (const Z& a) {
  58. return inv(a.v, Mod);
  59. }
  60. friend Z operator - (const Z& a) {
  61. return Z(0) - a;
  62. }
  63. Z& operator += (Z b) {
  64. return *this = *this + b;
  65. }
  66. Z& operator -= (Z b) {
  67. return *this = *this - b;
  68. }
  69. Z& operator *= (Z b) {
  70. return *this = *this * b;
  71. }
  72. friend boolean operator == (const Z& a, const Z& b) {
  73. return a.v == b.v;
  74. }
  75. };
  76.  
  77. typedef Z<> Zi;
  78.  
  79. Zi qpow(Zi a, int p) {
  80. if (p < Mod - 1)
  81. p += Mod - 1;
  82. Zi rt = 1, pa = a;
  83. for ( ; p; p >>= 1, pa = pa * pa) {
  84. if (p & 1) {
  85. rt = rt * pa;
  86. }
  87. }
  88. return rt;
  89. }
  90.  
  91. const Zi inv2 ((Mod + 1) >> 1);
  92.  
  93. class NTT {
  94. private:
  95. Zi gn[bzmax + 4], _gn[bzmax + 4];
  96. public:
  97.  
  98. NTT() {
  99. for (int i = 0; i <= bzmax; i++) {
  100. gn[i] = qpow(Zi(g), (Mod - 1) >> i);
  101. _gn[i] = qpow(Zi(g), -((Mod - 1) >> i));
  102. }
  103. }
  104.  
  105. void operator () (Zi* f, int len, int sgn) {
  106. for (int i = 1, j = len >> 1, k; i < len - 1; i++, j += k) {
  107. if (i < j)
  108. swap(f[i], f[j]);
  109. for (k = len >> 1; k <= j; j -= k, k >>= 1);
  110. }
  111.  
  112. Zi *wn = (sgn > 0) ? (gn + 1) : (_gn + 1), w, a, b;
  113. for (int l = 2, hl; l <= len; l <<= 1, wn++) {
  114. hl = l >> 1, w = 1;
  115. for (int i = 0; i < len; i += l, w = 1) {
  116. for (int j = 0; j < hl; j++, w *= *wn) {
  117. a = f[i + j], b = f[i + j + hl] * w;
  118. f[i + j] = a + b;
  119. f[i + j + hl] = a - b;
  120. }
  121. }
  122. }
  123.  
  124. if (sgn < 0) {
  125. Zi invlen = ~Zi(len);
  126. for (int i = 0; i < len; i++) {
  127. f[i] *= invlen;
  128. }
  129. }
  130. }
  131.  
  132. int correct_len(int len) {
  133. int m = 1;
  134. for ( ; m <= len; m <<= 1);
  135. return m;
  136. }
  137. } NTT;
  138.  
  139. void pol_inverse(Zi* f, Zi* g, int n) {
  140. static Zi A[N];
  141. if (n == 1) {
  142. g[0] = ~f[0];
  143. } else {
  144. int hn = (n + 1) >> 1, t = NTT.correct_len(n << 1 | 1);
  145. pol_inverse(f, g, hn);
  146.  
  147. pcopy(A, A + n, f);
  148. pfill(A + n, A + t, Zi(0));
  149. pfill(g + hn, g + t, Zi(0));
  150. NTT(A, t, 1);
  151. NTT(g, t, 1);
  152. for (int i = 0; i < t; i++) {
  153. g[i] = g[i] * (Zi(2) - g[i] * A[i]);
  154. }
  155. NTT(g, t, -1);
  156. pfill(g + n, g + t, Zi(0));
  157. }
  158. }
  159.  
  160. void pol_sqrt(Zi* f, Zi* g, int n) {
  161. static Zi A[N], B[N];
  162. if (n == 1) {
  163. g[0] = f[0];
  164. } else {
  165. int hn = (n + 1) >> 1, t = NTT.correct_len(n + n);
  166.  
  167. pol_sqrt(f, g, hn);
  168.  
  169. pfill(g + hn, g + n, Zi(0));
  170. for (int i = 0; i < hn; i++)
  171. A[i] = g[i] + g[i];
  172. pfill(A + hn, A + t, Zi(0));
  173. pol_inverse(A, B, n);
  174. pcopy(A, A + n, f);
  175. pfill(A + n, A + t, Zi(0));
  176. NTT(A, t, 1);
  177. NTT(B, t, 1);
  178. for (int i = 0; i < t; i++)
  179. A[i] *= B[i];
  180. NTT(A, t, -1);
  181. for (int i = 0; i < n; i++)
  182. g[i] = g[i] * inv2 + A[i];
  183. }
  184. }
  185.  
  186. typedef class Poly : public vector<Zi> {
  187. public:
  188. using vector<Zi>::vector;
  189.  
  190. Poly& fix(int sz) {
  191. resize(sz);
  192. return *this;
  193. }
  194. } Poly;
  195.  
  196. Poly operator + (Poly A, Poly B) {
  197. int n = A.size(), m = B.size();
  198. int t = max(n, m);
  199. A.resize(t), B.resize(t);
  200. for (int i = 0; i < t; i++) {
  201. A[i] += B[i];
  202. }
  203. return A;
  204. }
  205.  
  206. Poly operator - (Poly A, Poly B) {
  207. int n = A.size(), m = B.size();
  208. int t = max(n, m);
  209. A.resize(t), B.resize(t);
  210. for (int i = 0; i < t; i++) {
  211. A[i] -= B[i];
  212. }
  213. return A;
  214. }
  215.  
  216. Poly sqrt(Poly a) {
  217. Poly rt (a.size());
  218. pol_sqrt(a.data(), rt.data(), a.size());
  219. return rt;
  220. }
  221.  
  222. Poly operator * (Poly A, Poly B) {
  223. int n = A.size(), m = B.size();
  224. int k = NTT.correct_len(n + m - 1);
  225. if (n < 20 || m < 20) {
  226. Poly rt (n + m - 1);
  227. for (int i = 0; i < n; i++) {
  228. for (int j = 0; j < m; j++) {
  229. rt[i + j] += A[i] * B[j];
  230. }
  231. }
  232. return rt;
  233. }
  234. A.resize(k), B.resize(k);
  235. NTT(A.data(), k, 1);
  236. NTT(B.data(), k, 1);
  237. for (int i = 0; i < k; i++) {
  238. A[i] *= B[i];
  239. }
  240. NTT(A.data(), k, -1);
  241. A.resize(n + m - 1);
  242. return A;
  243. }
  244.  
  245. Poly operator ~ (Poly f) {
  246. int n = f.size(), t = NTT.correct_len((n << 1) | 1);
  247. Poly rt (t);
  248. f.resize(t);
  249. pol_inverse(f.data(), rt.data(), n);
  250. rt.resize(n);
  251. return rt;
  252. }
  253.  
  254. Poly operator / (Poly A, Poly B) {
  255. int n = A.size(), m = B.size();
  256. if (n < m) {
  257. return Poly {0};
  258. }
  259. int r = n - m + 1;
  260. reverse(A.begin(), A.end());
  261. reverse(B.begin(), B.end());
  262. A.resize(r), B.resize(r);
  263. A = A * ~B;
  264. A.resize(r);
  265. reverse(A.begin(), A.end());
  266. return A;
  267. }
  268.  
  269. Poly operator % (Poly A, Poly B) {
  270. int n = A.size(), m = B.size();
  271. if (n < m) {
  272. return A;
  273. }
  274. if (m == 1) {
  275. return Poly {0};
  276. }
  277. A = A - A / B * B;
  278. A.resize(m - 1);
  279. return A;
  280. }
  281.  
  282. Zi Inv[N];
  283. void init_inv(int n) {
  284. Inv[0] = 0, Inv[1] = 1;
  285. for (int i = 2; i <= n; i++) {
  286. Inv[i] = Inv[Mod % i] * Zi((Mod - (Mod / i)));
  287. }
  288. }
  289.  
  290. void diff(Poly& f) {
  291. if (f.size() == 1) {
  292. f[0] = 0;
  293. return;
  294. }
  295. for (int i = 1; i < (signed) f.size(); i++) {
  296. f[i - 1] = f[i] * Zi(i);
  297. }
  298. f.resize(f.size() - 1);
  299. }
  300. void integ(Poly& f) {
  301. f.resize(f.size() + 1);
  302. for (int i = (signed) f.size() - 1; i; i--) {
  303. f[i] = f[i - 1] * Inv[i];
  304. }
  305. f[0] = 0;
  306. }
  307.  
  308. Poly ln(Poly f) {
  309. int n = f.size();
  310. Poly h = f;
  311. diff(h);
  312. f = h * ~f;
  313. f.resize(n - 1);
  314. integ(f);
  315. return f;
  316. }
  317.  
  318. void pol_exp(Poly& f, Poly& g, int n) {
  319. Poly h;
  320. if (n == 1) {
  321. g.resize(1);
  322. g[0] = 1;
  323. } else {
  324. int hn = (n + 1) >> 1;
  325. pol_exp(f, g, hn);
  326.  
  327. h.resize(n), g.resize(n);
  328. pcopy(h.data(), h.data() + n, f.data());
  329.  
  330. g = g * (Poly{1} - ln(g) + h);
  331. g.resize(n);
  332. }
  333. }
  334.  
  335. Poly exp(Poly f) {
  336. int n = f.size();
  337. Poly rt;
  338. pol_exp(f, rt, n);
  339. return rt;
  340. }
  341.  
  342. class PolyBuilder {
  343. protected:
  344. int num;
  345. Poly P[N << 1];
  346.  
  347. void _init(int *x, int l, int r) {
  348. if (l == r) {
  349. P[num++] = Poly{-Zi(x[l]), Zi(1)};
  350. return;
  351. }
  352. int mid = (l + r) >> 1;
  353. int curid = num++;
  354. _init(x, l, mid);
  355. int rid = num;
  356. _init(x, mid + 1, r);
  357. P[curid] = P[curid + 1] * P[rid];
  358. }
  359.  
  360. void _evalute(Poly f, Zi* y, int l, int r) {
  361. f = f % P[num++];
  362. if (l == r) {
  363. y[l] = f[0];
  364. return;
  365. }
  366. int mid = (l + r) >> 1;
  367. _evalute(f, y, l, mid);
  368. _evalute(f, y, mid + 1, r);
  369. }
  370. public:
  371. Poly evalute(Poly f, int* x, int n) {
  372. Poly rt(n);
  373. num = 0;
  374. _init(x, 0, n - 1);
  375. num = 0;
  376. _evalute(f, rt.data(), 0, n - 1);
  377. return rt;
  378. }
  379. } PolyBuilder;
  380.  
  381. ostream& operator << (ostream& os, Poly& f) {
  382. for (auto x : f)
  383. os << x.v << ' ';
  384. os << '\n';
  385. return os;
  386. }
  387.  
  388. Zi fac[N], _fac[N];
  389. void init_fac(int n) {
  390. fac[0] = 1;
  391. for (int i = 1; i <= n; i++) {
  392. fac[i] = fac[i - 1] * i;
  393. }
  394. _fac[n] = ~fac[n];
  395. for (int i = n; i; i--) {
  396. _fac[i - 1] = _fac[i] * i;
  397. }
  398. }
  399.  
  400. int w;
  401. Poly dividing(int* a, int l, int r) {
  402. if (l == r)
  403. return Poly {a[l], 1};
  404. int mid = (l + r) >> 1;
  405. return dividing(a, l, mid) * dividing(a, mid + 1, r);
  406. }
  407. int xs[N];
  408. Poly work(vector<int> a, int maxseg) {
  409. if (!a.size()) {
  410. Poly rt (maxseg, Zi(0));
  411. rt[0] = 1;
  412. return rt;
  413. }
  414. for (auto& x : a)
  415. (x < 0) && (x = -x);
  416. vector<int> b {0};
  417. sort(a.begin(), a.end());
  418. int l = 0, r = (signed) a.size() - 1;
  419. while (l <= r) {
  420. if (1ll * a[l] * a[r] > w) {
  421. b.push_back(b.back() - 1);
  422. r--;
  423. } else {
  424. b.push_back(b.back() + 1);
  425. l++;
  426. }
  427. }
  428. b.pop_back();
  429. for (auto& x : b)
  430. (x < 0) && (x += Mod);
  431. Poly f = dividing(b.data(), 0, (signed) b.size() - 1);
  432. f = PolyBuilder.evalute(f, xs, maxseg);
  433. for (int i = 0; i < (signed) f.size(); i++) {
  434. f[i] *= _fac[i];
  435. }
  436. Poly g (f.size());
  437. for (int i = 0; i < (signed) g.size(); i++) {
  438. g[i] = _fac[i];
  439. (i & 1) && (g[i] = -g[i], 0);
  440. }
  441. f = (f * g).fix(maxseg);
  442. for (int i = 0; i < (signed) f.size(); i++)
  443. f[i] *= fac[i];
  444. return f;
  445. }
  446.  
  447. int n;
  448. int a[N];
  449. int main() {
  450. scanf("%d%d", &n, &w);
  451. vector<int> A, B;
  452. for (int i = 1; i <= n; i++) {
  453. scanf("%d", a + i);
  454. if (a[i] < 0) {
  455. A.push_back(a[i]);
  456. } else {
  457. B.push_back(a[i]);
  458. }
  459. }
  460. init_fac(n + 3);
  461. int maxseg = min(A.size(), B.size()) + 3;
  462. for (int i = 1; i < maxseg; i++)
  463. xs[i] = i;
  464. Poly f = work(A, maxseg);
  465. Poly g = work(B, maxseg);
  466. Zi ans = 0;
  467. for (int i = 0; i < maxseg; i++) {
  468. ans += f[i] * g[i] * 2;
  469. if (i)
  470. ans += f[i] * g[i - 1];
  471. if (i < maxseg - 1)
  472. ans += f[i] * g[i + 1];
  473. }
  474. printf("%d\n", ans.v);
  475. return 0;
  476. }

luogu P5606 小 K 与毕业旅行 - 构造 - 多项式的更多相关文章

  1. [Luogu] P1993 小K的农场

    题目描述 小K在MC里面建立很多很多的农场,总共n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共m个),以下列三种形式描述: 农场a比农场b至少多种植了c个单位的作 ...

  2. Luogu P1993 小 K 的农场

    其实很早以前就打好了,但一直忘记写了. 也就是差分约束的模板题. 关于差分约束,也就是用来求关于一些不等式互相约束算出最优解. 推荐一个讲的很好的博客:http://www.cppblog.com/m ...

  3. 【luogu P1993 小K的农场】 题解

    题目链接:https://www.luogu.org/problemnew/show/P1993 1.差分约束: 对于a - b <= c 有一条 b-->a 权值为c 对于a - b & ...

  4. luogu 1993 小K的农场

    差分约束+spfa判负环 dfs判负环 #include<bits/stdc++.h> #define rep(i,x,y) for(register int i=x;i<=y;i+ ...

  5. 『题解』洛谷P1993 小K的农场

    更好的阅读体验 Portal Portal1: Luogu Description 小\(K\)在\(\mathrm MC\)里面建立很多很多的农场,总共\(n\)个,以至于他自己都忘记了每个农场中种 ...

  6. Luogu 1081 【NOIP2012】开车旅行 (链表,倍增)

    Luogu 1081 [NOIP2012]开车旅行 (链表,倍增) Description 小A 和小B决定利用假期外出旅行,他们将想去的城市从1到N 编号,且编号较小的城市在编号较大的城市的西边,已 ...

  7. BZOJ-1143&&BZOJ-2718 祭祀river&&毕业旅行 最长反链(Floyed传递闭包+二分图匹配)

    蛋蛋安利的双倍经验题 1143: [CTSC2008]祭祀river Time Limit: 10 Sec Memory Limit: 162 MB Submit: 1901 Solved: 951 ...

  8. BZOJ2718: [Violet 4]毕业旅行

    2718: [Violet 4]毕业旅行 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 229  Solved: 126[Submit][Status ...

  9. 洛谷P1993 小K的农场 [差分约束系统]

    题目传送门 小K的农场 题目描述 小K在MC里面建立很多很多的农场,总共n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共m个),以下列三种形式描述: 农场a比农场b ...

随机推荐

  1. poj-3404 Bridge over a rough river Ad Hoc

    Bridge over a rough river POJ - 3404 Bridge over a rough river Time Limit: 1000MS   Memory Limit: 65 ...

  2. Ubuntu18.04下修改快捷键

    Ubuntu下修改快捷键 Intelij Idea在Ubuntu下的快捷键几乎和windows差不多,最常用的一个快捷键与系统冲突: Ctrl + Alt + T idea是surround with ...

  3. Autoware 培训笔记 No. 3——录制航迹点

    1.前言 航迹点用于知道汽车运行,autoware的每个航迹点包含x, y, z, yaw, velocity信息. 航迹点录制有两种方式,可以开车录制航迹点,也可以采集数据包,线下录制航迹点,我分开 ...

  4. 一个简单的利用 WebClient 异步下载的示例(二)

    继上一篇 一个简单的利用 WebClient 异步下载的示例(一) 后,我想把核心的处理提取出来,成 SkyWebClient,如下: 1. SkyWebClient 该构造函数中 downloadC ...

  5. 发布.net core项目 System.AggregateException: 发生一个或多个错误

    背景:之前创建.net core webapi项目的时候SDK是2.2的版本,后改成2.1,发布的时候报错. 发布的时候报错,展示的信息是: 其实这里也大致能看到部分信息,但由于信息量太小,没办法知道 ...

  6. CentOS系统安装Python3

    准备: CentOS 6.4系统 Python-3.6.5.tgz 下载地址: 官网:https://www.python.org/downloads/release/python-365/ 镜像:h ...

  7. Java中级知识归纳(二)

    六.Java中Collection和Collections的区别? java.util.Collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法. java.util.Coll ...

  8. jsonHelper帮助类

    使用前,需引用开源项目类using Newtonsoft.Json 链接:https://pan.baidu.com/s/1htK784XyRCl2XaGGM7RtEg 提取码:gs2n using ...

  9. 续~ES6 新语法 (symbol、set集合、 数组对象的filter(),reduce(),weakset(),map()方法)

    一.symbol 回顾数据类型: 基本类型(原始类型): String Number Boolean Undifined Null Symbol 引用类型: Objects 1.1 创建symbol ...

  10. js字符串转为数字方法parseInt()、减号、乘号、JSON.parse()、Number()的效率比较

    var a = '1'; var b = '0x1'; var runTest = function(timeTag, testFunction) { console.time(timeTag); f ...