写的时候要些变换主要就是抓住1.明白自变量的范围。2.卷积里两个自变量的和是一个常数。3.长度时大于等于实际的二倍的。
还有这题网上大多数的题目推导都是有问题的(导致常数过大,推导是正确的),或者有些小哥的姿势不对,也导致常数过大然后就卡了。。。。尼玛,卡常啊这题。。。。。。分析看下面:
对于任意模数,如果我们要求ans%p,p是任意模数,但是我们只能求mod费马素数的ntt,解决办法就是先算出真正值即还木有模p之前的值,然后再模p这样的话就好了。
先选三个费马素数 m1,m2,m3然后求出ans(modm1*m2*m3)由于ans的最大值都是小于m1*m2*m3的,所以ans%(m1*m2*m3)=ans然后求出来后再ans%p就好了,阔能要高精度
然后就搞吧。
这题的特点就是当你求出了此题的式子以后你会发现变量中的下标是和位置有关的,这时你需要做的就是做变量代换将下标换到和式中
比如此题就是
A[i] = revJ[i]; B[n - i] = ((J[i-1] * pow2[n - i]) % mod)*a[i]; 你会发现如果我们让i+k=t 然后 和式就是 A[i]*B[n-t]的形式 i是0-n-k变化的 而且 i+t=n-k是固定的所以现在就变好了注意t当然是大于等于1的,把我下面推导的公式换下就好了,这样写好理解一点
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const long long mod = 998244353;//
const long long g = 3;
long long n;
long long last = 0, llast = 1;
long long pow2[120000], revJ[120000], J[120000], wn[100];
long long A[400500], B[400500], a[400500], f[400500];
void exgcd(long long a, long long b, long long &x, long long &y)
{
if (b == 0)
{
x = 1, y = 0; return;
}
else
{
exgcd(b, a%b, y, x);
long long kk1 = (a / b) % mod; long long kk2 = x%mod;
y -= kk1*kk2;//这不是y-=a/b;
y = (y%mod + mod) % mod;
}
}
long long pow(long long value, long long times)
{
long long ans = 1; long long temp = value;
for (; times; times = times / 2)
{
if (times & 1)ans = (ans*temp) % mod;
temp = (temp*temp) % mod;
}
return ans;
}
void rander(long long *a, long long len)
{
for (long long i = 1, j = len / 2; i < len - 1; i++)
{
if (i < j)
{
swap(a[i], a[j]);
}
long long N = len / 2;
while (N <= j)
{
j -= N;
N /= 2;
}
j += N;
}
}
void init(int len)
{
if (last < len)
last = len;
else
return;
long long rr, ss;
for (int i = llast; i <= last; i++)
{
exgcd(i, mod, rr, ss);
revJ[i] = (revJ[i - 1] * rr) % mod;
pow2[i] = (pow2[i - 1] * 2) % mod;
J[i] = (J[i - 1] * i) % mod;
}
}
void ntt(long long *a, long long len, int kind)
{
rander(a, len); long long change1, change2;
for (int s = 1; (1 << s) <= len; s++)
{
long long m = (1 << s);
for (int k = 0; k < len; k += m)
{
long long w = 1;
for (int j = k; j < k + m / 2; j++)
{
long long left = a[j];
long long right = (w*a[j + m / 2]) % mod;
a[j] = (left + right) % mod; a[j + m / 2] = ((left - right) % mod + mod) % mod;
w = (w*wn[s]) % mod;
}
}
}
if (kind == -1)
{
for (int i = 1; i < len / 2; i++)swap(a[i], a[len - i]);
long long tempp; long long t = 1;
exgcd(len, mod, t, tempp);
for (int i = 0; i < len; i++)
a[i] = (t*(a[i] % mod)) % mod;
}
}
int main()
{
for (int i = 0; i<21; i++)
{
int t = 1 << i;
wn[i] = pow(g, (mod - 1) / t);
}
int t;
scanf("%d", &t);
revJ[0] = 1; J[0] = 1; pow2[0] = 1;
while (t--)
{
scanf("%d", &n);
//n = 20;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);//a[i] = 1000000000;
sort(a + 1, a + n + 1, greater());
init(n);
for (int i = 0; i < n; i++)
{
A[i] = revJ[i];
//B[n - 1 - i] = (((J[i] * pow2[n - 1 - i]) % mod) * a[i]) % mod;
}
for (int i = 1; i <= n; i++)
B[n - i] = ((J[i-1] * pow2[n - i]) % mod)*a[i];
long long len = 1;
while (len / 2 < (n))len *= 2;
for (int i = n; i < len; i++)A[i] = 0, B[i] = 0;//为什么要这一步?因为在推导fft的时候你会发现我们用的是矩阵来运算的所以不能只考虑w1 w2 ..wn-1而忽略了wn..wlen。
ntt(A, len, 1);
ntt(B, len, 1);
for (int i = 0; i
注意B[n-1-i]=...中的A[i]与上面的A[i]不是同一个。。。。为什么这样写网上有很多题解,就是公式推导后常数有点大,改成我这样就好 了!!!!!!
这里推荐一个小哥的,只是他的代码过不了看分析就好http://blog.csdn.net/cqu_hyx/article/details/52194696