2020.3.1考试T1 多项式

出题人很凉心的把算法写成了题目名

首先我们可以发现每一维的贡献是独立的,这可以从 \(solve1\) 里看出来

然后我们可以考虑转化为 \(DP\) ,这可以从 \(solve2\) 里看出来

我们统计每一维能产生的贡献,就是 \(a\) 个 \(0\) 面, \(b\) 个 \(1\) 面, \(c\) 个 \(2\) 面这种形式,能写成一个多项式 \(ax^0+bx^1+cx^2\),而我们最终显然就是把所有的多项式都乘起来。

暴力一个一个乘就很 naive,分治 \(NTT\) 解决就好啦。

不透彻的话把每个 \(solve\) 都看一遍就好啦。

Warning:请不要学习本代码的分治 \(NTT\) 写法,考场上现想出来的,实现麻烦了不少,建议学一下其他大佬的写法。

#include<iostream>
#include<cstdio>
#define int long long
#define LL long long
using namespace std;
int n;
const int N = 400010, mod = 469762049, G = 3, Ginv = (mod + 1) / 3;
int a[N], b[N], c[N], ans[N];
inline int read()
{
	int res = 0; char ch = getchar(); bool XX = false;
	for (; !isdigit(ch); ch = getchar())(ch == ‘-‘) && (XX = true);
	for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
	return XX ? -res : res;
}
void solve1()
{
	int tmp;
	for (int i = 1; i <= a[1]; ++i)
	{
		tmp = 0;
		if (i == 1)++tmp; if (i == a[1])++tmp;
		++ans[tmp];
	}
	for (int i = 0; i <= 2 * n; ++i)printf("%lld\n", ans[i]);
}
void solve2()
{
	int tmp;
	for (int i = 1; i <= a[1]; ++i)
		for (int j = 1; j <= a[2]; ++j)
		{
			tmp = 0;
			if (i == 1)++tmp; if (i == a[1])++tmp;
			if (j == 1)++tmp; if (j == a[2])++tmp;
			++ans[tmp];
		}
	for (int i = 0; i <= 2 * n; ++i)printf("%lld\n", ans[i]);
}
void solve3()
{
	int tmp;
	for (int i = 1; i <= n; ++i)
	{
		tmp = a[i]; a[i] = b[i] = c[i] = 0;
		if (tmp == 1)a[i] = 1, c[i] = 0;
		else b[i] = 2, c[i] = tmp - b[i];
	}
	ans[0] = 1;
	for (int i = 1; i <= n; ++i)
	{
		for (int j = 2 * n; j >= 2; --j)
			ans[j] = ((LL)ans[j] * c[i] % mod + (LL)ans[j - 1] * b[i] % mod + (LL)ans[j - 2] * a[i] % mod) % mod;
		ans[1] = ((LL)ans[1] * c[i] % mod + (LL)ans[0] * b[i] % mod) % mod;
		ans[0] = (LL)ans[0] * c[i] % mod;
	}
	for (int i = 0; i <= 2 * n; ++i)printf("%lld\n", ans[i]);
}

/*下边 solve4*/

int last, top;
int r[N], zhan[30], tmp[500];
LL ksm(LL a, LL b, LL mod)
{
	LL res = 1;
	for (; b; b >>= 1, a = a * a % mod)
		if (b & 1)res = res * a % mod;
	return res;
}

void NTT(LL *A, int lim, int opt)
{
	if (last != lim)
	{
		last = lim;
		for (int i = 0; i < lim; ++i)
			r[i] = (r[i >> 1] >> 1) | (i & 1 ? (lim >> 1) : 0);
	}
	for (int i = 0; i < lim; ++i)
		if (i < r[i])swap(A[i], A[r[i]]);
	int len;
	LL wn, w, x, y;
	for (int mid = 1; mid < lim; mid <<= 1)
	{
		len = mid << 1;
		wn = ksm(opt == 1 ? G : Ginv, (mod - 1) / len, mod);
		for (int j = 0; j < lim; j += len)
		{
			w = 1;
			for (int k = j; k < j + mid; ++k, w = w * wn % mod)
			{
				x = A[k]; y = A[k + mid] * w % mod;
				A[k] = (x + y) % mod;
				A[k + mid] = (x - y + mod) % mod;
			}
		}
	}
	if (opt == 1)return;
	int ni = ksm(lim, mod - 2, mod);
	for (int i = 0; i < lim; ++i)A[i] = A[i] * ni % mod;
}
void MUL(LL *A, int n, LL *B, int m)
{
	if (n + m <= 115)
	{
		for (int i = 0, to = n + m; i <= to; ++i)tmp[i] = 0;
		for (int i = 0; i <= n; ++i)
			for (int j = 0; j <= m; ++j)
				(tmp[i + j] += A[i] * B[j] % mod) %= mod;
		for (int i = 0, to = n + m; i <= to; ++i)A[i] = tmp[i];
		for (int i = 0; i <= m; ++i)B[i] = 0;
	}
	else
	{
		int lim = 1;
		while (lim <= (n + m))lim <<= 1;
		NTT(A, lim, 1); NTT(B, lim, 1);
		for (int i = 0; i < lim; ++i)A[i] = A[i] * B[i] % mod, B[i] = 0;
		NTT(A, lim, -1);
	}
}
struct dxs
{
	int siz;
	LL v[N];
} A[30];
int newdxs()
{
	return zhan[top--];
}
void huidxs(int x)
{
	A[x].siz = 0;
	zhan[++top] = x;
}
int solve(int l, int r)
{
	if (l == r)
	{
		int k = newdxs();
		A[k].siz = 2;
		A[k].v[0] = c[l]; A[k].v[1] = b[l]; A[k].v[2] = a[l];
		return k;
	}
	int mid = (l + r) >> 1;
	int lson = solve(l, mid), rson = solve(mid + 1, r);
	MUL(A[lson].v, A[lson].siz, A[rson].v, A[rson].siz);
	A[lson].siz = A[lson].siz + A[rson].siz;
	huidxs(rson);
	return lson;
}
void solve4()
{
	int tmp;
	for (int i = 1; i <= n; ++i)
	{
		tmp = a[i]; a[i] = b[i] = c[i] = 0;
		if (tmp == 1)a[i] = 1, c[i] = 0;
		else b[i] = 2, c[i] = tmp - b[i];
	}
	for (int i = 1; i <= 25; ++i)zhan[++top] = i;
	int k = solve(1, n);
	for (int i = 0; i <= 2 * n; ++i)printf("%lld\n", A[k].v[i]);
}

/*上边 solve4*/

signed main()
{
	freopen("poly.in", "r", stdin);
	freopen("poly.out", "w", stdout);
	cin >> n;
	for (int i = 1; i <= n; ++i)
	{
		a[i] = read();
	}
	if (n == 1 && a[1] <= 1000)solve1();
	else if (n == 2 && a[1] <= 1000 && a[2] <= 1000)solve2();
	else if (n <= 5000)solve3();
	else solve4();
	fclose(stdin); fclose(stdout);
	return 0;
}

原文地址:https://www.cnblogs.com/wljss/p/12628999.html

时间: 2024-08-30 17:42:59

2020.3.1考试T1 多项式的相关文章

脱产备考CPA是孤注一掷?应该如何备考2020年注册会计师考试?

在浩浩荡荡的CPA备考大军中,有朝九晚五想要寻求职业突破的上班族,有家务孩子一把抓的全职妈妈,也有想要奋力一搏的脱产人员--背景不同,面临的问题当然有所不同.而与其他人相比,脱产备考的考生心理压力总是格外的重,毕竟没有工作,没有收入,甚至可能得不到家人朋友的理解,如此全力以赴.孤注一掷的冒险,无非是希望自己能比别人多占据一些备考优势,那么脱产人员应该如何备考2020年注册会计师考试呢?老实说,对于选择脱产备考的人来说,时间确实足够充裕,但要想把时间充分利用起来,争取一次性过五科甚至六科,以下几个

某考试T1 game

题目背景 无 题目描述 Alice 和 Bob 在一个圆环上玩游戏.圆环上有 n 个位置,按照顺时针顺序 依次标号为 1 到 n.Alice 和 Bob 分别有一个数字集合,集合中都是在 [1, n?1] 内的正整数.游戏开始时会有一个棋子摆在圆环上的某个位置,然后两人轮流 行动.轮到某个人的回合时,他可以从他的集合中选出某个数 x,然后把棋子 沿顺时针方向移动 x 个位置.如果某个人把棋子移动到了 1 号位置,那么他就 获胜了.两个人都会以最优策略行动. 你需要对不同先后手顺序以及棋子初始位置

某考试 T1 sigfib

设 g(x) = f(x) * x ,多项式 A = Σ g(i) * x^i , 多项式  B = Σ f(i) * x^i. 首先,g(x) = g(x-1) + g(x-2) + f(x-1) + 2f(x-2),所以我们可以得到: A = x * A + x^2 * A + x * B + 2 * x^2 * B + x 又因为B是斐波那契数列的多项式,所以B的闭形式可以直接得到,就是  x/(1-x-x^2)   [这个也不难推,可以自己推推]. 于是我们可以开开心心的解出A的闭形式,

2020.1.2考试总结

T1圆圈游戏 暴力DP有60分,设包含圆i的最小的圆是fa[i],那没最终会的得到一棵树,对于一棵子树,选了根节点就不能选子树内其它点,f[i]=max(w[i],\(\sum f[son]\)). 瓶颈就在怎么建图,因为圆不相交相切,所以扫描线的时候相对位置不会发生改变,用set维护一下就好啦. #include<algorithm> #include<iostream> #include<cmath> #include<queue> #include&l

2020.1.5考试总结

T1和T3不太可做..先只放一下T2 TMD考场上没算好空间直接MLE爆零... 操作1可以归到操作3里,并且几个人的操作可以合并到一块,用线段树挺好维护的. 对于询问的话可以对每一个节点开一个桶,记录区间内前缀数量,向上合并的时候左儿子直接加,右儿子异或后再加. 发现很多节点根本用不到,动态开点即可. #include<iostream> #include<cstring> #include<cstdio> #define lson (k<<1) #def

2020.1.9考试总结

T1 数据范围很合适.. 第一档就是暴力枚举 第二档就是数位DP 第三档就是矩阵乘法 丢一下学长的博客 #include<iostream> #include<cstring> #include<cstdio> #define LL long long using namespace std; LL n; const int mod = 1e9 + 7; struct ju { LL c[4][4]; friend ju operator *(const ju &

2020.1.11考试总结

恭贺 treAKer 在毒瘤之神的考验一题中rank1,成为新一届毒瘤之神! ...... 结果今天就考了 treAKer 的毒瘤题... T1 考场上看到1e6就想O(n)的做法,结果失败了... 正解思路很神奇,就是先对物品按照a来排序,询问按照m来排序,用双指针一起扫,同时维护\(f_i\) 当物品的和为i时 在所有的方案中,最小的b最大 的方案中的 b值.判一下\(f_i\)和m+s的大小关系即可. #include<algorithm> #include<cstring>

16.1112 模拟考试 T1

加密[问题描述]有一种不讲道理的加密方法是: 在字符串的任意位置随机插入字符. 相应的,不讲道理的解密方法就是从字符串中恰好删去随机插入的那些字符.给定原文s和加密后的字符串t,求?有多少子串可以通过解密得到原文s.[输入格式]输入第一行包含一个字符串t,第二行包含一个字符串s.[输出格式]输出一行,包含一个整数,代表可以通过解密得到原文的s的子串的数量.[样例输入]abcabcabccba[样例输出]9[样例解释]用[?,?]表示子串开头结尾的下标(从 0 开始编号) ,这 9 种方案是:[0

某考试 T1 Hello my friend

Discription 原文地址:https://www.cnblogs.com/JYYHH/p/8870108.html