参考
参考题解1
上面那位博主其实说的很仔细,可能多项式有点晦涩,但理解之后发现博主确实说的挺详细的。
计算 g ( x ) g(x) g(x)
这道题多项式的第一个难点在于如何计算多项式
g
(
x
)
g(x)
g(x)的各项系数。
根据题目描述,可以知道
g
(
x
)
=
(
x
−
3
)
⋅
(
x
−
3
2
)
⋅
.
.
.
⋅
(
x
−
3
k
)
g(x)=(x-3)·(x-3^2)·...·(x-3^k)
g(x)=(x−3)⋅(x−32)⋅...⋅(x−3k)
我记得老师教过一个方法,就是说如果要计算括号展开后,
x
j
x^j
xj的系数
a
j
a_j
aj,那么
a
j
a_j
aj一定是由形如
x
m
⋅
x
n
,
s
.
t
.
m
+
n
=
j
x^m·x^n,s.t. m+n=j
xm⋅xn,s.t.m+n=j这些幂次构成的,所以某一项的系数其实由下面给出:
a
j
=
∑
∀
m
,
n
,
m
+
n
=
j
a
m
⋅
a
n
a_j=\sum_{{{\forall}}m,n, \\m+n=j}a_m·a_n
aj=∀m,n,m+n=j∑am⋅an
但一口气算完是不现实的,其实可以按照两个括号两个括号的逐次合并。一共有 k k k个括号,那么需要合并 k − 1 k-1 k−1次。
比如我们先合并
q
2
(
x
)
=
(
x
−
3
)
⋅
(
x
−
3
2
)
q_2(x)=(x-3)·(x-3^2)
q2(x)=(x−3)⋅(x−32),
再合并
q
3
(
x
)
=
q
2
(
x
)
⋅
(
x
−
3
3
)
q_3(x)=q_2(x)·(x-3^3)
q3(x)=q2(x)⋅(x−33)于是变成了一个动态规划。这道题可以看到,含有
x
x
x的每个括号其次数都为1,那么就可以先确定出初始状态
G
[
k
]
=
−
3
,
G
[
k
−
1
]
=
1
G[k]=-3,G[k-1]=1
G[k]=−3,G[k−1]=1
我们欲从低次 ( x − 3 ) (x-3) (x−3)推的 x k x^k xk的系数 G [ 0 ] G[0] G[0],从而确定出每一次数的系数。
再以上面合并 q 2 ( x ) = ( x − 3 ) ( x − 3 2 ) q_2(x)=(x-3)(x-3^2) q2(x)=(x−3)(x−32)时为例,可以看到这两项合并最高次数为2,并且只能由 x ⋅ x x·x x⋅x构成。所以可以填入 G [ k − 2 ] = 1 G[k-2]=1 G[k−2]=1
由于当前合并的是第1个括号和第2个括号,那么此时还有i=0,所以准确来说是 G [ k − 2 − i ] = 1 G[k-2-i]=1 G[k−2−i]=1
对于
q
2
(
x
)
q_2(x)
q2(x)的其他幂次,比如
x
=
x
1
x=x^1
x=x1,那么
q
2
(
x
)
q_2(x)
q2(x)的一次项系数,它可以由第一个括号的x乘上第二个括号的系数(
−
9
-9
−9),和第二个括号的
x
x
x乘上第一个括号的系数
(
−
3
)
(-3)
(−3)相加得到。
按道理来说这里如果括号里也是个多项式,那就很麻烦了,好在他每个括号都是一次的。所以合并之后的
x
j
x_j
xj一定来自于先前
x
j
x_j
xj的系数乘上待合并括号的常数,再加上原来
x
j
−
1
x_{j-1}
xj−1的系数乘上新括号
x
x
x的系数(恒等于1)相加得到。
为了代码实现,所以在每次合并括号,都会先记录一下当前多项式,以免覆盖。
多项式相除
这便是第二个难点了。我比较笨,看着博主代码想了很久。事实上代码的多项式相除比手算简便,因为手算还需要确定该乘多少次才能对其。但事实上,代码实现时,只关心当前该乘多少系数,也就是当前被除数最高位的系数。
因为我们用数组来表示多项式了,乘 x x x的幂次的操作不过是数组的对其方式罢了。事实上用第0位表示当前多项式最高次,就隐含了对齐这个操作了。
存放checksum
按道理可以在 D ( x ) D(x) D(x)上原地更新存放校验码,但是直接将 D ( x ) D(x) D(x)多开几位( k − 1 k-1 k−1次多项式便是长度为k的数组)使得k-1次多项式能够存下就好了,以免折腾。
到此这道题就完成的差不多了。
代码
#include <iostream>
#include <vector>
#include <valarray>
using namespace std;
int w, s;
int UPPER = 0;
int LOWER = 1;
int DIGIT = 2;
void
coding(string &input, vector<int> &vec) {
int state = UPPER;
for (int i = 0; i < input.length(); ++i) {
if ('A' <= input[i] && input[i] <= 'Z') {
// UPPER
if (state == LOWER) {
// LOWER -> DIGIT -> UPPER
vec.push_back(28);
vec.push_back(28);
} else if (state == DIGIT) {
vec.push_back(28);
}
vec.push_back(input[i] - 'A');
state = UPPER;
} else if ('a' <= input[i] && input[i] <= 'z') {
if (state != LOWER) {
vec.push_back(27);
}
vec.push_back(input[i] - 'a');
state = LOWER;
} else if ('0' <= input[i] && input[i] <= '9') {
if (state != DIGIT) {
vec.push_back(28);
}
vec.push_back(input[i] - '0');
state = DIGIT;
}
}
}
int mazi(int a, int b) {
return 30 * a + b;
}
void
mazi(vector<int> &code, vector<int> &ma) {
for (int i = 0; i < code.size(); i += 2) {
ma.push_back(mazi(code[i], code[i + 1]));
}
}
ostream &operator<<(ostream &os, vector<int> &m) {
for (auto &x: m) {
cout << x << "\n";
}
return os;
}
int MOD = 929;
int main() {
cin >> w >> s;
string raw_str;
cin >> raw_str;
vector<int> coded;
coding(raw_str, coded);
if (coded.size() % 2 == 1) {
coded.push_back(29);
}
vector<int> mazied;
mazi(coded, mazied);
int k = s != -1 ? pow(2, s + 1) : 0;
while ((mazied.size() + k + 1) % w != 0) {
mazied.push_back(900);
}
cout << mazied.size() + 1 << "\n";
if (s != -1) {
// construct k polynomial, G[0] means the x^k, G[1] means x^{k-1}, ..., x^2, x ,C
vector<int> G(k + 1, 0);
G[k] = -3;
G[k - 1] = 1;
// (x^2 - 9)
int tmp_C = -9;
// calc our G(x), follow (x-3) * (x-3^2) ..
for (int i = 0; i < k - 1; ++i) {
vector<int> old_G(k + 1);
for (int j = k - i - 1; j <= k; ++j) {
old_G[j] = G[j];
}
/**
* for example:
* (x-3)(x-3^2)
* we know G[k] = -3,G[k-1] = 1 ( as we see (x-3) here)
* then going to multiply to (x^2 - 9) (why tmp_C = - 9 here)
* update C(x^1) = G[k-1] = -9 + (-3) = G[k-1] * tmp_C + G[k-2] = G[k-1] * tmp_C + old_G[k]
* */
for (int j = k - i - 1; j <= k; ++j) {
G[j] = (G[j] % MOD) * (tmp_C % MOD) % MOD;
}
for (int j = k - i - 1; j < k; ++j) {
// consider lower index will also construct this
G[j] += old_G[j + 1] % MOD;
}
// as we multiply each loop,
// the current highest must construct by all x param, so must be 1
G[k - i - 2] = 1;
tmp_C *= 3;
tmp_C %= MOD;
}
vector<int> D(mazied.size() + 1 + k,0);
D[0] = mazied.size() + 1;
for (int i = 0; i < mazied.size(); ++i) {
D[i+1] = mazied[i];
D[i+1] %= MOD;
}
auto polynomial_divide = [&k, &mazied](vector<int> &Dx, vector<int> &Gx) {
/**
* Dx/Gx
* */
// the divide means, we will align the low index to high poly index,
// so for each param in Gx, we will multiply them as D(x) current high poly param
// i=0 for the highest index
for (int i = 0; i < Dx.size() - k; ++i) {
int current_C = Dx[i];
// multiply all poly in Gx by C
// Gx[0] is x^k
for (int j = 0; j < Gx.size(); ++j) {
Dx[i + j] = Dx[i + j] - ((Gx[j] * current_C) % MOD);
Dx[i + j] %= MOD;
}
}
//update checksum
for (long long i = Dx.size() - Gx.size() + 1; i < Dx.size(); ++i) {
if (-Dx[i] < 0)
mazied.push_back(MOD+ (-Dx[i] % MOD));
else
mazied.push_back(-Dx[i] % MOD);
}
// cout << Gx;
};
// cout << "D=" << D;
// cout << "G=" << G;
polynomial_divide(D, G);
}
cout << mazied;
}