• 多边形序列(组合数)(高精)(NTT)


    多边形序列

    题目大意

    一个由 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;
    }
    
  • 相关阅读:
    面向对象第三单元总结与心得体会
    面向对象第二单元总结与心得体会
    面向对象第一单元总结与心得体会
    20183411 李丞灏 2019-2020 《python程序设计》 实验四 爬取QQ音乐中周杰伦歌单 实验报告
    20183411 李丞灏 2020-2021 《python程序设计》 实验三 加密传输文件 实验报告
    20183411 李丞灏 2019-2020《Python程序设计》实验二 报告
    20183411 李丞灏 2019-2020《Python程序设计》 实验一 报告
    OO第四单元 UML、封装以及课程总结
    OO第三单元 规格、JML、测试
    OO Project2 电梯、多线程Overview
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/jzoj_4758.html
Copyright © 2020-2023  润新知