多邊形序列(組合數)(高精)(NTT)
阿新 • • 發佈:2021-08-24
一個由 LR 組成的序列,可以構造一個多邊形,L 表示 90 度的角,R 表示 270 度的。
然後一個可視的多邊形是你可以在多邊形內部找到一個點可以看到多邊形的每個點。
給你序列長度,問你有多少種序列滿足可以構成的直角多邊形中有可視的。
多邊形序列
題目大意
一個由 LR 組成的序列,可以構造一個多邊形,L 表示 90 度的角,R 表示 270 度的。
然後一個可視的多邊形是你可以在多邊形內部找到一個點可以看到多邊形的每個點。
給你序列長度,問你有多少種序列滿足可以構成的直角多邊形中有可視的。
思路
你看一下圖,你會發現一個東西,就是它不可能出現凸字形的。
你看這個圖形,它如果要左邊凸出來被看到,就一定要在粉色的區域,如果有右邊凸出來被看到,就一定要在棕色的區域,你無論在哪個位置,都會有一個凸出來的看不到。
那你搞一搞會發現它其實就是一堆 LR(RL),然後裡面有四個 L 來轉方向。
然後不難看到不能有兩個 R 在一起,因為就會出現凹字形。
那就是 \(\dfrac{n-4}{2}\) 個 R,\(\dfrac{n+4}{2}\) 個 L,R 不能放在一起。
不難想到插空法得到組合數,但是你還要去一種不合法的。
因為你是環形,你是不可以在最兩邊都放 R 的。
所以你要一種是一定要一邊,一種是一個都不要。
然後就是 \(C_{x}^4+C_{x-1}^4\)。
然後就要用高精,乘法就要用 FFT 或者 NTT,我這裡用的是 NTT。
程式碼
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long #define mo 998244353 #define G 3 using namespace std; struct gj { int n; ll a[800001]; }n, x, x1, x2, x3; char s[100001]; int sn, limit, ln, an[800001]; ll Gv; ll ksm(ll x, ll y) { ll re = 1; while (y) { if (y & 1) re = (re * x) % mo; x = (x * x) % mo; y >>= 1; } return re; } void chu(gj &now, int x) { for (int i = now.n; i >= 0; i--) { if (i) now.a[i - 1] += 10 * (now.a[i] % x); now.a[i] = now.a[i] / x; } while (!now.a[now.n]) now.n--; } void jian(gj &now, int x) { now.a[0] -= x; int tmp = 0; while (now.a[tmp] < 0) { now.a[tmp + 1] -= -1 * now.a[tmp] / 10; now.a[tmp] += -1 * now.a[tmp] / 10 * 10; if (now.a[tmp] < 0) now.a[tmp + 1]--, now.a[tmp] += 10; tmp++; } while (!now.a[now.n]) now.n--; } void NTT(gj &now, int op) { for (int i = 0; i < limit; i++) if (i < an[i]) swap(now.a[i], now.a[an[i]]); for (int mid = 1; mid < limit; mid <<= 1) { ll Wn = ksm(op == 1 ? G : Gv, (mo - 1) / (mid << 1)); for (int R = (mid << 1), j = 0; j < limit; j += R) { ll w = 1; for (int k = 0; k < mid; k++, w = w * Wn % mo) { ll x = now.a[j + k], y = w * now.a[j + mid + k] % mo; now.a[j + k] = (x + y) % mo; now.a[j + mid + k] = (x - y + mo) % mo; } } } } void CHENG(gj &x, gj y) { limit = 1; ln = 0; while (limit <= x.n + y.n) { limit <<= 1; ln++; } for (int i = 0; i < limit; i++)//高精乘用 NTT 加速 an[i] = (an[i >> 1] >> 1) | ((i & 1) << (ln - 1)); NTT(x, 1); NTT(y, 1); for (int i = 0; i < limit; i++) x.a[i] = (x.a[i] * y.a[i]) % mo; NTT(x, -1); ll limv = ksm(limit, mo - 2); for (int i = 0; i <= x.n + y.n + 1; i++) x.a[i] = (x.a[i] * limv) % mo; for (int i = 0; i <= x.n + y.n + 1; i++) {//記得進位 x.a[i + 1] += x.a[i] / 10; x.a[i] %= 10; } x.n = x.n + y.n + 1; while (!x.a[x.n]) x.n--; } int main() { Gv = ksm(G, mo - 2); scanf("%s", &s); sn = strlen(s) - 1; if ((s[sn] - '0') & 1) { printf("0"); return 0; } n.n = sn; for (int i = 0; i <= sn; i++) n.a[sn - i] = s[i] - '0'; x = n; x.a[0] += 4; int tmp = 0; while (x.a[tmp] > 9) { x.a[tmp + 1] += x.a[tmp] / 10; x.a[tmp] %= 10; tmp++; } if (tmp > x.n) x.n = tmp; chu(x, 2);//得到 x=(n+4)/2 x1 = x2 = x3 = x; jian(x1, 1); jian(x2, 2); jian(x3, 3);//得到 x-1,x-2,x-3 CHENG(x1, x2); CHENG(x1, x2); CHENG(x1, x3); chu(x1, 12);//得到 ans=(x-1)(x-2)^2(x-3)/12 for (int i = x1.n; i >= 0; i--) printf("%lld", x1.a[i]); return 0; }