Problem
f(cos(x))=cos(n∗x) holds for all x.
Given two integers n and m, you need to calculate the coefficient of x^m in f(x), modulo 998244353.
Input Format
Multiple test cases (no more than 100).
Each test case contains one line consisting of two integers n and m.
1≤n≤109,0≤m≤104
Output Format
Output the answer in a single line for each test case.
样例输入
2 0
2 1
2 2
样例输出
998244352
0
2
题目来源
2017 ACM-ICPC 亚洲区(西安赛区)网络赛
Solution&Code
先说题意,这是个考察第一类切比雪夫多项式的问题。对于n次多项式f(n),要求给出f(n)中次数为m的那一项的系数。
开始解题:
cos(2x) = 2cos²(x)-1.
cos(4x) = 2cos²(2x)-1 = 2(2cos²(x)-1)²-1 = 8cos(x)^4-8cos²(x)+1.
∵ sin(2x) = 2sin(x)cos(x),sin(4x) = 2sin(2x)cos(2x) = 4sin(x)cos(x)(2cos²(x)-1).
∴ cos(5x) = cos(4x)cos(x)-sin(4x)sin(x) = 8cos(x)^5-8cos³(x)+cos(x)-4sin²(x)cos(x)(2cos²(x)-1)= 8cos(x)^5-8cos³(x)+cos(x)-4(1-cos²(x))(2cos²(x)-1)cos(x)
= 16cos(x)^5-20cos³(x)+5cos(x)
……
更一般的结论:
f(0)=1;
f(1)=x;
f(n)=2x*(n-1)-f(n-2),n≥2.
e.g.
f(0)=1;
f(1)=x;
f(2)=2x2-1;
f(3)=4x3-3x;
f(4)=8x4-8x2+1;
f(5)=16x5-20x3+5x;
……
考虑到m的规模不超过1e4,n的规模不超过1e9,可以借助公式求解:
有些情况是可以特判的:
n为奇数m为偶数,或者n为偶数m为奇数,答案是0.
n为偶数m为0,当 4|n时,答案是1;否则是-1.
n为奇数m为1,答案是n.
注:维基百科的《切比雪夫多项式(Chebyshev polynomials)》链接
https://en.wikipedia.org/wiki/Chebyshev_polynomials
AC code 1(一如上述,用到了Lucas求大组合数):
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 998244353ll;
ll fun(ll y,ll n) {
ll s=1;
while (n) {
if (n&1) s=s*y%mod;
y=y*y%mod;
n>>=1;
}
return s;
}
ll Comb(ll a, ll b, ll p) {
if (a < b) return 0;
if (a == b) return 1;
if (b > a-b) b = a-b;
ll ans = 1, ca = 1, cb = 1;
for (ll i = 0; i < b; ++i) {
ca = (ca * (a - i))%p;
cb = (cb * (b - i))%p;
}
ans = (ca*fun(cb, p - 2)) % p;
return ans;
}
ll Lucas(ll n,ll m,ll p) {
ll ans = 1;
while (n!=0&&m!=0&&ans!=0) {
ans = (ans*Comb(n%p, m%p, p)) % p;
n /= p;
m /= p;
}
return ans;
}
ll n,m,k,ans;
int main() {
while(~scanf("%lld%lld",&n,&m)) {
if (n%2==0&&m%2!=0||n%2!=0&&m%2==0||m>n) {
printf("0\n");
continue;
}
if (m==0) {
if (n%4==0) printf("1\n");
else printf("%lld\n",mod-1);
continue;
}
k=(n-m)>>1;
ans = fun(2,n-k-k-1) * Lucas(n-k,k,mod) % mod * n % mod * fun(n-k,mod-2) % mod;
if (k&1) ans*=-1;
printf("%lld\n", (ans+mod)%mod);
}
return 0;
}
AC code 2(按照队友的思路写的双阶乘)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn = 20005,mod = 998244353;
ll a[maxn];
void init() {
ll tmp = 1;
a[0] = 1;
ll len = 20002;
for(ll i=1 ; i<=len ; i++) {
tmp = (tmp * i) % mod;
a[i] = tmp;
// cout << a[i] << endl;
}
}
ll fun(ll y,ll n) {
ll s=1;
while (n) {
if (n&1) s=s*y%mod;
y=y*y%mod;
n>>=1;
}
return s;
}
ll calc(ll x, ll y) {
ll tmp = 1;
for( ; x > y ; x -= 2) {
tmp = (tmp * x)%mod;
}
return tmp;
}
int main() {
init();
ll n, k;
ll tmp;
while(~scanf("%lld%lld",&n,&k)) {
if(n%2==0&&k%2!=0||n%2!=0&&k%2==0) {
printf("0\n");
continue;
}
if (k == 0) {
if (n%4==0) printf("1\n");
else printf("%lld\n",mod-1);
continue;
}
if(n&1) {
if (k&1) {
k = (k+1)>>1;
if(((n+1-2*k)>>1)&1) tmp = -1;
else tmp = 1;
ll b = calc(n+2*k-3, n+1-2*k);
tmp *= (((b * n) % mod) * fun(a[2*k-1], mod-2))%mod;
} else tmp = 0;
} else {
if (k&1) tmp = 0;
else {
k>>=1;
if(((n-2*k)>>1)&1) tmp = -1;
else tmp = 1;
ll b = calc(n+2*k-2, n-2*k);
tmp *= (((b * n) % mod) * fun(a[2*k], mod-2))%mod;
}
}
printf("%lld\n", (tmp+mod)%mod);
}
return 0;
}