题意: 有$n$种球,每种有无限个,同时第$i$种球有一个代价$c_i$,你要拿不超过$w$个球。如果最后第$i$种球你拿了$k_i$个,那么你会获得$\prod_{1\leq
跑的比较快的多项式板子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
#include <cstring> #include <iostream> #include <random> using int_t = long long int; using std::cin; using std::cout; using std::endl; const int_t LARGE = 2e6; const int_t mod = 998244353; const int_t g = 3; int_t power(int_t b, int_t i) { int_t r = 1; if (i < 0) i = (i % (mod - 1) + mod - 1) % (mod - 1); while (i) { if (i & 1) r = r * b % mod; b = b * b % mod; i >>= 1; } return r; } void makeflip(int_t* arr, int_t size2) { int_t len = (1 << size2); arr[0] = 0; for (int_t i = 1; i < len; i++) { arr[i] = (arr[i >> 1] >> 1) | ((i & 1) << (size2 - 1)); } } int_t upper2n(int_t x) { int_t r = 0; while ((1 << r) < x) r++; return r; } template <int_t arg = 1> void transform(int_t* A, int_t size2, int_t* flip) { int_t len = (1 << size2); for (int_t i = 0; i < len; i++) { int_t r = flip[i]; if (r > i) std::swap(A[i], A[r]); } for (int_t i = 2; i <= len; i *= 2) { int_t mr = power(g, arg * (mod - 1) / i); for (int_t j = 0; j < len; j += i) { int_t curr = 1; for (int_t k = 0; k < i / 2; k++) { int_t u = A[j + k], v = curr * A[j + k + i / 2] % mod; A[j + k] = (u + v) % mod; A[j + k + i / 2] = (u - v + mod) % mod; curr = curr * mr % mod; } } } } void poly_inv(const int_t* A, int_t n, int_t* result) { /* 这里的n是模x^n 计算B(x)*A(x) = 1 mod x^n, 其中A(x)已知 假设已知A(x)*B(x) = 1 mod x^{ceil(n/2)} 假设C(x)*A(x) = 1 mod x^n (A(x)B(x)-1)^2 = A^2(x)B^2(x)-2A(x)B(x)+1= 0 A(x)B^2(x)-2B(x)+C(x) = 0 C(x) = 2B(x)-A(x)B^2(x) */ // #ifdef DEBUG // cout << "at " << n << endl; // #endif if (n == 1) { result[0] = power(A[0], -1); return; } int_t next = n / 2 + n % 2; poly_inv(A, next, result); //次数不要选错了,应该用n次的A和B去卷 int_t size2 = upper2n(n + 2 * next + 1); static int_t X[LARGE]; static int_t Y[LARGE]; int_t len = (1 << size2); // 写错设置范围了! memset(X + n, 0, sizeof(X[0]) * (len - n)); memset(Y + next, 0, sizeof(Y[0]) * (len - next)); memcpy(X, A, sizeof(A[0]) * n); memcpy(Y, result, sizeof(result[0]) * next); static int_t fliparr[LARGE]; makeflip(fliparr, size2); transform<1>(X, size2, fliparr); transform<1>(Y, size2, fliparr); for (int_t i = 0; i < len; i++) { X[i] = (2 * Y[i] - X[i] * Y[i] % mod * Y[i] % mod + mod) % mod; } transform<-1>(X, size2, fliparr); const int_t inv = power(len, -1); for (int_t i = 0; i < n; i++) result[i] = X[i] * inv % mod; #ifdef DEBUG cout << "poly inv at " << n << endl; cout << "result = "; for (int_t i = 0; i < next; i++) cout << result[i] << " "; cout << endl; #endif } int_t poly_divat(const int_t* F, const int_t* G, int_t n, int_t k) { /* n次多项式F和G 计算F(x)/G(x)的k次项前系数 考虑F(x)*G(-x)/G(x)*G(-x),分母只有偶数次项,写为C(x^2);分子写成xA(x^2)+B(x^2),如果k是奇数,那么递归(A,C,n,k/2),如果k是偶数,那么递归(B,C,n,k/2) 到k<=n时直接计算 */ int_t size2 = upper2n(2 * n + 1); int_t len = 1 << size2; static int_t fliparr[LARGE]; makeflip(fliparr, size2); static int_t T1[LARGE], T2[LARGE], T3[LARGE]; for (int_t i = 0; i < len; i++) { if (i <= n) T1[i] = F[i], T2[i] = G[i]; else T1[i] = T2[i] = T3[i] = 0; } const int_t inv = power(len, -1); while (k >= n) { #ifdef DEBUG cout << "curr at k = " << k << endl; #endif for (int_t i = 0; i < len; i++) { if (i <= n) { T3[i] = T2[i] * (i % 2 ? (mod - 1) : 1); } else T3[i] = 0; } transform(T1, size2, fliparr); transform(T2, size2, fliparr); transform(T3, size2, fliparr); for (int_t i = 0; i < len; i++) { T1[i] = T1[i] * T3[i] % mod; T2[i] = T2[i] * T3[i] % mod; } transform<-1>(T1, size2, fliparr); transform<-1>(T2, size2, fliparr); #ifdef DEBUG cout << "prod T1 = "; for (int_t i = 0; i < len; i++) cout << T1[i] * inv % mod << " "; cout << endl; cout << "prod T2 = "; for (int_t i = 0; i < len; i++) cout << T2[i] * inv % mod << " "; cout << endl; #endif for (int_t i = 0; i < len; i++) { if (i * 2 < len) { T2[i] = T2[i * 2] * inv % mod; } else T2[i] = 0; } int_t b = k % 2; for (int_t i = 0; i < len; i++) { if (i % 2 == b) { T1[i / 2] = T1[i] * inv % mod; #ifdef DEBUG cout << "at " << i << " assgin " << T1[i] * inv << " to " << i / 2 << endl; #endif } #ifdef DEBUG cout << "assign 0 to " << i << endl; #endif if (i > 0) T1[i] = 0; } #ifdef DEBUG cout << "finished T1 = "; for (int_t i = 0; i < len; i++) cout << T1[i] % mod << " "; cout << endl; cout << "finished T2 = "; for (int_t i = 0; i < len; i++) cout << T2[i] % mod << " "; cout << endl; #endif k >>= 1; } poly_inv(T2, k + 1, T3); // #ifdef DEBUG // cout << "finished k = " << k << endl; // cout << "T1 = "; // for (int_t i = 0; i < len; i++) // cout << T1[i] % mod << " "; // cout << endl; // cout << "T2 = "; // for (int_t i = 0; i < len; i++) // cout << T2[i] % mod << " "; // cout << endl; // cout << "T2 inv = "; // for (int_t i = 0; i < len; i++) // cout << T3[i] % mod << " "; // cout << endl; // #endif int_t result = 0; //计算结果的k次项 for (int_t i = 0; i <= k; i++) { result = (result + T1[i] * T3[k - i] % mod) % mod; } return result; } void poly_mul(const int_t* A, int_t n, const int_t* B, int_t m, int_t* C) { /* 计算n次多项式A与m次多项式B的乘积 */ int_t size2 = upper2n(n + m + 1); int_t len = 1 << size2; static int_t T1[LARGE], T2[LARGE]; for (int_t i = 0; i < len; i++) { if (i <= n) T1[i] = A[i]; else T1[i] = 0; if (i <= m) T2[i] = B[i]; else T2[i] = 0; } static int_t fliparr[LARGE]; makeflip(fliparr, size2); transform(T1, size2, fliparr); transform(T2, size2, fliparr); for (int_t i = 0; i < len; i++) T1[i] = T1[i] * T2[i] % mod; transform<-1>(T1, size2, fliparr); int_t inv = power(len, -1); for (int_t i = 0; i <= n + m; i++) C[i] = T1[i] * inv % mod; } int_t poly_linear_rec(const int_t* A0, const int_t* F0, int_t n, int_t k) { /* 计算线性递推 F[1],F[2]...F[k] 递推系数 A[0],A[1]...A[k-1] 首项 A[m]=A[m-1]*F[1]+A[m-2]*F[2]+...+A[m-k]*F[k] */ static int_t T1[LARGE], T2[LARGE]; static int_t Ax[LARGE], Fx[LARGE]; Fx[0] = 0; Ax[k] = 0; for (int i = 1; i <= k; i++) { Fx[i] = F0[i]; Ax[i - 1] = A0[i - 1]; } poly_mul(Ax, k, Fx, k, T1); T1[0] = Ax[0]; for (int i = 1; i <= k - 1; i++) { T1[i] = (Ax[i] - T1[i] + mod) % mod; } for (int i = k; i <= 2 * k; i++) T1[i] = 0; T2[0] = 1; for (int i = 1; i <= k; i++) T2[i] = (mod - Fx[i]) % mod; // #ifdef DEBUG // cout << "T1 = "; // for (int_t i = 0; i <= k; i++) { // cout << T1[i] << " "; // } // cout << endl; // cout << "T2 = "; // for (int_t i = 0; i <= k; i++) { // cout << T2[i] << " "; // } // cout << endl; // #endif return poly_divat(T1, T2, k, n); } void poly_log(const int_t* A, int_t n, int_t* out) { /* 计算log(A(x)), A(x)为n次多项式 DlogF(x) =DF(x)/F(x) */ static int_t Ad[LARGE]; static int_t Finv[LARGE]; static int_t R[LARGE]; const int_t m = n - 1; for (int_t i = 0; i <= m; i++) { Ad[i] = A[i + 1] * (i + 1) % mod; } Ad[n] = 0; poly_inv(A, n + 1, Finv); poly_mul(Ad, m, Finv, n, R); for (int_t i = 1; i <= n; i++) { out[i] = R[i - 1] * power(i, -1) % mod; } } void poly_exp(const int_t* A, int_t n, int_t* out) { /* 计算exp(A(x)), A(x)为n次多项式 H(x)=G(x)(1-logG(x)+A(x)) H(x)为当次递推项,G(x)为上一次递推项 */ if (n == 1) { out[0] = 1; return; } int_t r = n / 2 + n % 2; poly_exp(A, r, out); static int_t G[LARGE]; static int_t G2[LARGE]; static int_t logG[LARGE]; static int_t R[LARGE]; for (int_t i = 0; i < r; i++) G[i] = out[i]; for (int_t i = r; i < n; i++) G[i] = 0; poly_log(G, n - 1, logG); // for (int_t i = r; i < n; i++) // logG[i] = 0; for (int_t i = 0; i < n; i++) { G2[i] = (mod - logG[i] + A[i]) % mod; } G2[0] = (G2[0] + 1) % mod; poly_mul(G, n - 1, G2, n - 1, R); for (int_t i = 0; i < n; i++) out[i] = R[i]; #ifdef DEBUG cout << "at " << n << endl; cout << "A = "; for (int_t i = 0; i < n; i++) cout << A[i] << " "; cout << endl; cout << "G = "; for (int_t i = 0; i < n; i++) cout << G[i] << " "; cout << endl; cout << "logG = "; for (int_t i = 0; i < n; i++) cout << logG[i] << " "; cout << endl; cout << "G2 = "; for (int_t i = 0; i < n; i++) cout << G2[i] << " "; cout << endl; cout << "R = "; for (int_t i = 0; i < n; i++) cout << R[i] << " "; cout << endl; cout << endl; #endif } int main() { std::ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int_t n; cin >> n; static int_t A[LARGE], B[LARGE]; static int_t C[LARGE]; for (int_t i = 0; i < n; i++) cin >> A[i]; poly_exp(A, n, B); for (int_t i = 0; i < n; i++) cout << B[i] << " "; return 0; } |
常系数线性递推的新做法 – 计算[x^k]P(x)/Q(x)
$$ a_n=\sum_{1\le i\le k}{f_ia_{n-i}} \\ \left\{ a_0,a_1....a_{k-1} \right\} \text{已知} \\ \\ \text{设}F\left(
Nginx路由匹配模拟器
https://nginx.viraptor.info/
真是我的救星,调路由调吐了
CFGYM103104I-WHUPC I Sequence
CF1521C Nastia and a Hidden Permutation
真是个沙比提,比赛的时候也想出来了,但是因为太迷糊了没写完
CFGYM102394L CCPC2019哈尔滨 L题 LRU Algorithm
CFGYM102394E CCPC2019哈尔滨 E题 Exchanging Gifts
假设我们能维护出最终序列的长度L和最终序列出现最多的数的个数cnt,假设$cnt\leq\frac L 2$ ,那么答案是L,这时候我们把序列升序和降序对起来就构造出n的结果。
假设$cnt>\frac L 2$,那么答案是$2(L-cnt)$,我们仍然把序列升序和降序对应起来,答案是$L-(cnt-(L-cnt))=2*(L-cnt)$
比如
1 2 3 3 3 3 3
3 3 3 3 3 2 1
中间有3个3是重了的,那么重的部分有cnt-(L-cnt)个,其中L-cnt表示的是1 2的长度,所以总答案是L-(cnt-(L-cnt))
现在的问题在于如何维护出这个众数,并且判定$cnt\leq \frac L 2$是否成立。
考虑一种线性时间求求序列众数(出现次数大于一半)的方法:
令f(i)表示我们从头开始扫到第i个元素的时候(用$a_i$表示),序列中出现次数最多的数与其他的数出现次数之和的差值。同时我们需要维护这个数是多少(用x来表示)。
每次扫到$a_i$的时候:
- 如果$a_i=x$,那么f(i)=f(i-1)+1,x不变
- 如果$a_i\neq x$,那么f(i)=f(i-1)-1,然后如果$f(i)<0$,那么x变为$a_i$,同时$f(i)$取反。
对于这个题,如果我们要使用这种求众数的方法,核心在于如何考虑操作2(合并两个序列时)如何处理。
我们维护每个序列的f和x,合并两个序列的时候:
- 如果他们的f相同,那么新序列的f不变,x相加。
- 如果他们的f不同,那么考虑下两个序列的哪一个的x比较大。如果他们相同,那么新序列的f就写为0(这时候可能存在两个众数),x从原来的x里随便选一个。如果他们不同,那x选择为f较大的那一个,同时f设置为较大值减掉较小值。
所以对于这个题,我们先用这种求众数的方法算出来最终序列的众数。
但此时求出来的众数,仅仅是在保证存在一个出现次数超过序列长度一半时候的众数,如果不存在这样子的众数,也会得出来一个结果,但是并不具有意义。
所以我们再跑一遍递推,求出来我们上一步求的众数的出现次数,然后我们就可以照着前文所述来算答案了。
*此外,这个题卡常*
我跑了半天性能分析后大概知道了几个卡常的点:
1. 输入量非常巨大,请考虑一次性读入输入数据后自行用缓冲区处理输入。
2. std::vector的构造函数非常慢(性能分析显示,$10^6$次调用大约花了80ms),所以不要使用vector来存储变长的序列,考虑自行分配-回收内存。
3. State结构体的构造函数占了大概40ms的时间,考虑强制内联。
另外,读入函数千万不要写错了!不要写成chr>='9'!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
#pragma GCC optimize("O2") #include <assert.h> #include <inttypes.h> #include <algorithm> #include <iostream> #include <vector> using int_t = int; using std::cin; using std::cout; using std::endl; const int_t LARGE = 1e6; char inputbuf[(int(2e8) / 1024) * 1024]; char* head = inputbuf; inline char nextchar() { return *(head++); } void initinput() { fread(inputbuf, 1024, sizeof(inputbuf) / 1024, stdin); } struct State { int64_t len; int_t mostval; int64_t mostcount; inline State(int64_t len = 0, int_t mostval = 0, int64_t mostcount = 0) : len(len), mostval(mostval), mostcount(mostcount) {} State operator+(const State& rhs) const { State result(len + rhs.len, 0, 0); if (mostval == rhs.mostval) result.mostval = mostval, result.mostcount = mostcount + rhs.mostcount; else { if (mostcount > rhs.mostcount) { result.mostcount = mostcount - rhs.mostcount; result.mostval = mostval; } else { result.mostcount = rhs.mostcount - mostcount; result.mostval = rhs.mostval; } } return result; } }; std::ostream& operator<<(std::ostream& os, const State& state) { os << "State{len=" << state.len << ",mostval=" << state.mostval << ",mostcount=" << state.mostcount << "}"; return os; } struct Opt { int_t type; int_t* data; int_t datalen; int_t x1, x2; int64_t mostcount = 0; } opts[LARGE + 1]; State dp[LARGE + 1]; int_t n; template <class T> void read(T& x) { x = 0; char chr = nextchar(); while (chr < '0' || chr > '9') chr = nextchar(); while (chr >= '0' && chr <= '9') { x = x * 10 + chr - '0'; chr = nextchar(); } assert(x >= 0); } template <class T> void write(T x) { assert(x >= 0); if (x > 9) write(x / 10); putchar('0' + x % 10); } int main() { // freopen("input.txt", "r", stdin); initinput(); int_t T; read(T); while (T--) { read(n); for (int_t i = 1; i <= n; i++) { auto& ref = opts[i]; if (ref.data) { delete[] ref.data; ref.data = nullptr; } dp[i] = State(); read(ref.type); if (ref.type == 1) { int_t k; read(k); int_t sum = 0, val = 0; ref.data = new int_t[k + 1]; ref.datalen = k; for (int_t i = 1; i <= k; i++) { int_t x; read(x); ref.data[i] = x; if (x == val) sum++; else sum--; if (sum < 0) { sum *= -1, val = x; } } // ref.seq.shrink_to_fit(); dp[i] = State(k, val, sum); } else { read(ref.x1); read(ref.x2); } } for (int_t i = 1; i <= n; i++) { const auto& ref1 = opts[i]; if (ref1.type == 2) { dp[i] = dp[ref1.x1] + dp[ref1.x2]; } } #ifdef DEBUG for (int_t i = 1; i <= n; i++) { cout << "dp " << i << " = " << dp[i] << endl; } #endif int_t mostval = dp[n].mostval; for (int_t i = 1; i <= n; i++) { auto& ref = opts[i]; if (ref.type == 1) ref.mostcount = std::count(ref.data + 1, ref.data + 1 + ref.datalen, mostval); else ref.mostcount = opts[ref.x1].mostcount + opts[ref.x2].mostcount; } int64_t mostcount = opts[n].mostcount; #ifdef DEBUG cout << "final len " << dp[n].len << endl; cout << "mostcount " << mostcount << endl; cout << "mostval " << mostval << endl; #endif if (mostcount * 2 <= dp[n].len) { write(dp[n].len); } else { write(2 * (dp[n].len - mostcount)); } puts(""); } return 0; } |
CFGYM102394I CCPC2019哈尔滨 I题 Interesting Permutations
签到题我都不会做,我爬了。
首先检测一些明显非法的情况:
CFGYM102394A CCPC2019哈尔滨A Artful Paintings
一眼看过去是差分约束板子题,实际上也就是差分约束板子题。