QOJ.ac

QOJ

IDProblemSubmitterResultTimeMemoryLanguageFile sizeSubmit timeJudge time
#199206#6411. Classical FFT Problemucup-team870WA 97ms30204kbC++179.8kb2023-10-03 22:59:302023-10-03 22:59:31

Judging History

你现在查看的是最新测评结果

  • [2023-10-03 22:59:31]
  • 评测
  • 测评结果:WA
  • 用时:97ms
  • 内存:30204kb
  • [2023-10-03 22:59:30]
  • 提交

answer

#include<bits/stdc++.h>
using namespace std;
#define IOS {cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);}
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define P pair<int,int>
#define ll long long
#define vi vector<int>
const int N = 2e5+5, mod = 998244353, G = 3;
ll qp(ll x, ll y) {
    ll res = 1;
    while (y) {
        if (y & 1)res = res * x % mod;
        x = x * x % mod; y >>= 1;
    }return res;
}
const int Gi = qp(G, mod - 2);
typedef vi poly;
namespace Poly {
    int limit = 1, L = 0; int r[N * 4];
    void NTT(poly& A, int type) { //下标在[0,limit)范围内,数组开四倍即可
        A.resize(limit);
        for (int i = 0; i < limit; i++)
            if (i < r[i]) swap(A[i], A[r[i]]);
        for (int mid = 1; mid < limit; mid <<= 1) {
            ll Wn = qp(type == 1 ? G : Gi, (mod - 1) / (mid << 1)); //G是模数的原根,Gi是逆元
            for (int j = 0; j < limit; j += (mid << 1)) {
                ll w = 1; //ll不一定够
                for (int k = 0; k < mid; k++, w = (w * Wn) % mod) {
                    int x = A[j + k], y = w * A[j + k + mid] % mod; //int不一定够
                    A[j + k] = (x + y) % mod, A[j + k + mid] = (x - y + mod) % mod;
                }
            }
        }
    }
    poly operator + (poly a, poly b) {
        int n = max(a.size(), b.size());
        a.resize(n); b.resize(n);
        rep(i, 0, n - 1)a[i] = (a[i] + b[i]) % mod;
        return a;
    }
    poly operator - (poly a, poly b) {
        int n = max(a.size(), b.size());
        a.resize(n); b.resize(n);
        rep(i, 0, n - 1)a[i] = (a[i] - b[i] + mod) % mod;
        return a;
    }
    void poly_mul_init(poly& a, poly& b) {
        limit = 1; L = 0;
        int N = a.size() - 1, M = b.size() - 1;
        while (limit <= N + M) limit <<= 1, L++;
        for (int i = 0; i < limit; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (L - 1));
    }
    poly poly_mul(poly a, poly b) { //原先的a,b不需要维持原状的话,可以加&
        int n = a.size() + b.size() - 1;
        poly_mul_init(a, b);
        NTT(a, 1); NTT(b, 1);
        for (int i = 0; i < limit; i++) a[i] = 1ll * a[i] * b[i] % mod; //a[i]为ll不一定够
        NTT(a, -1);
        ll INV = qp(limit, mod - 2);
        rep(i, 0, limit - 1)a[i] = a[i] * INV % mod;
        a.resize(n); return a;
    }
    poly poly_inv(poly& a, int n) { //n为答案阶数, %(x^(n+1))意义下
        if (a.size() < n + 1)a.resize(n + 1); //!!!!
        if (n == 0)return { (int)qp(a[0],mod - 2) };
        int n2 = n / 2;
        poly b = poly_inv(a, n2);
        poly ta(n + 1); rep(i, 0, n)ta[i] = a[i];
        /*等价于:
        poly ans=poly_mul(ta,b); ans.resize(n+1);
        rep(i,0,n)ans[i]=(((i==0)?2:0)-ans[i]+mod)%mod;
        ans=poly_mul(b,ans); ans.resize(n+1); return ans;*/
        b.resize(n + 1); //ta和b的阶数一定要相同
        poly_mul_init(ta, b);
        NTT(ta, 1); NTT(b, 1);
        rep(i, 0, limit)b[i] = (2 - 1ll * ta[i] * b[i] % mod + mod) * b[i] % mod;  //蝴蝶变换这里每一项2-,然而多项式乘法时还是{2,0,0...}
        NTT(b, -1);
        ll inv = qp(limit, mod - 2);
        rep(i, 0, n)b[i] = b[i] * inv % mod;
        b.resize(n + 1); ta.clear(); ta.shrink_to_fit();
        return b;
    }
    poly poly_derivate(poly& a) { //求导
        int n = (int)a.size() - 1; poly da(n + 1);
        rep(i, 1, n)da[i - 1] = 1ll * a[i] * i % mod;
        return da;
    }
    poly poly_integral(poly& a) { //积分
        int n = (int)a.size(); poly ia(n + 1);
        rep(i, 1, n)ia[i] = 1ll * a[i - 1] * qp(i, mod - 2) % mod;
        return ia;
    }
    poly poly_ln(poly a) {
        int n = (int)a.size() - 1;
        assert(a[0] == 1);
        poly da = poly_derivate(a);
        a = poly_inv(a, n);
        poly b = poly_mul(a, da); b.resize(n + 1);
        vi res = poly_integral(b); res.resize(n + 1); return res;
    }
    poly poly_exp(poly& a, int n) {  //牛顿迭代, nxtg = g*(1-ln(g)+A)
        if (n == 0) {
            assert(a[0] == 0); return { 1 };
        }
        int n2 = n / 2;
        poly g = poly_exp(a, n2); g.resize(n + 1);//这里求逆要扩展g
        poly lng = poly_ln(g);
        rep(i, 0, n)lng[i] = ((i == 0) + a[i] - lng[i] + mod) % mod;
        poly res = poly_mul(g, lng); res.resize(n + 1);
        return res;
    }
    //多项式ln+exp可以用来优化多项式快速幂:f(x)^m = exp(ln(f(x))*m)
    poly poly_pow(poly a, int y) { //a[0]=1. a[0]=0的时候直接移位; 非0的时候整个多项式/a[0],最终结果再*qp(a[0],y)
        assert(a[0] == 1);
        int n = (int)a.size() - 1;
        poly lna = poly_ln(a);
        rep(i, 0, n)lna[i] = 1ll * lna[i] * y % mod;
        return poly_exp(lna, n);
    }
    void cdq_fft(poly& f, poly& g, int l, int r) { //f=F(f)*g形式,考虑[l,mid]对[mid+1,r]的贡献,一次poly_mul即可
        if (l == r)return;
        int mid = l + r >> 1;
        cdq_fft(f, g, l, mid);
        poly b(r - l + 1); rep(i, 0, r - l)b[i] = g[i];
        poly a(mid - l + 1); rep(i, 0, mid - l)a[i] = f[i + l];
        poly res = poly_mul(a, b);
        rep(i, mid + 1, r)f[i] = (f[i] + res[i - l]) % mod;
        cdq_fft(f, g, mid + 1, r);
    }
    pair<poly, poly>poly_divison(poly f, poly g) { //f=q*g+r, f(n),g(m),q(n-m),r(m-1)
        int m = (int)g.size() - 1;
        if (f.size() < m + 1)f.resize(m + 1); int n = (int)f.size() - 1;
        vi fR = f; reverse(fR.begin(), fR.end()); fR.resize(n - m + 1);//快了很多!!
        vi gR = g; reverse(gR.begin(), gR.end());
        auto qR = poly_mul(poly_inv(gR, n - m), fR); qR.resize(n - m + 1);
        auto q = qR; reverse(q.begin(), q.end());
        auto r = f - poly_mul(q, g); r.resize(m);
        return { q,r };
    }
    int linear_recurrence_poly(vi a, vi f, int n) { //常系数齐次线性递推,求a_n.  
        //a_i=sigma(a_{i-j}*f_j),1<=j<=k. 已知a的[0,k-1]项
        int k = a.size(); assert(f.size() == k + 1);
        if (n < k)return a[n];
        vi p(k + 1); p[k] = 1; rep(i, 1, k)p[k - i] = (mod - f[i]) % mod;
        vi res = { 1 }, x = { 0,1 };
        vi gR = p; reverse(gR.begin(), gR.end()); vi invgR = poly_inv(gR, k); //gR的逆预处理出来
        auto poly_division_r = [&](poly f, poly g)->poly {
            int m = (int)g.size() - 1;
            if (f.size() < m + 1)f.resize(m + 1); int n = (int)f.size() - 1;
            vi fR = f; reverse(fR.begin(), fR.end()); fR.resize(n - m + 1);
            auto qR = poly_mul(invgR, fR); qR.resize(n - m + 1);
            auto q = qR; reverse(q.begin(), q.end());
            auto r = f - poly_mul(q, g); r.resize(m);
            return r;
        };
        while (n) {
            int nn = x.size() * 2 - 1;
            poly_mul_init(x, x);
            NTT(x, 1);
            if (n & 1) {
                NTT(res, 1);
                for (int i = 0; i < limit; i++) res[i] = 1ll * res[i] * x[i] % mod;
            }
            for (int i = 0; i < limit; i++)x[i] = 1ll * x[i] * x[i] % mod;
            NTT(x, -1);
            ll INV = qp(limit, mod - 2);
            if (n & 1) {
                NTT(res, -1);
                for (int i = 0; i < limit; i++)res[i] = res[i] * INV % mod;
                res.resize(nn);
            }
            for (int i = 0; i < limit; i++)x[i] = x[i] * INV % mod;
            x.resize(nn);
            if (n & 1)res = poly_division_r(res, p); //四次NTT即可
            x = poly_division_r(x, p);
            n >>= 1;
        }
        ll ans = 0;
        rep(i, 0, k - 1)ans = (ans + 1ll * res[i] * a[i]) % mod; return ans;
    }
}
namespace multi_eval{//多点求值
    poly q[N*4]; //N=O(求值点)
    int xs[N],res[N]; //求值的点,答案
    void dfs(int i,int l,int r){
        if(l==r){
            q[i]={1,mod-xs[l]}; return;
        }
        int mid=l+r>>1;
        dfs(i<<1,l,mid); dfs(i<<1|1,mid+1,r);
        q[i]=Poly::poly_mul(q[i<<1],q[i<<1|1]);
    }
    poly MulT(poly a,poly b) {
        int n = a.size(), m = b.size(); 
        std::reverse(b.begin(),b.end()), b = Poly::poly_mul(a,b);
        for(int i = 0;i < n;i++) a[i] = b[i + m - 1];
        return a;
    }
    void slv(poly F,int i,int l,int r) {
        F.resize(r - l + 1);
        if(l == r) return void(res[l] = F[0]);
        int mid = (l + r) / 2;
        slv(MulT(F,q[i<<1|1]),i<<1,l,mid);
        slv(MulT(F,q[i<<1]),i<<1|1,mid + 1,r);
        return;
    }
    vi eval(poly a,vi ask){ //ask的下标[1,m]. 返回ans的下标[1,m]
        int m=(int)ask.size()-1; rep(i,1,m)xs[i]=ask[i];
        dfs(1,1,m);  slv( MulT(a,Poly::poly_inv(q[1],m)) ,1,1,m);
        vi ans(m+1); rep(i,1,m)ans[i]=res[i]; return ans;
    }
};
ll fac[N],inv[N];
ll C(int i,int j){
    return fac[i]*inv[j]%mod*inv[i-j]%mod;
}
poly dfs(vi &a, int l,int r){
    if(l==r){
        return {a[l],mod-1};
    }
    int mid=l+r>>1;
    return Poly::poly_mul(dfs(a,l,mid),dfs(a,mid+1,r));
}
ll cal(int k,vi a,int v){ //[1,k]个数, 包含[1,v]
    vi ask(v+2);
    rep(i,1,v+1)ask[i]=i-1;
    poly A=dfs(a,1,k);
    auto res=multi_eval::eval(A,ask);
    int fl=1; ll ans=0;
    per(i,v,0){
        ans+=fl*res[v-i+1]*C(v,i)%mod;
        fl=-fl;
    }
    return (ans%mod+mod)%mod;
}
int a[N];
int main(){
    IOS
    const int M=2e5;
    fac[0]=1; rep(i,1,M)fac[i]=fac[i-1]*i%mod;
    inv[M]=qp(fac[M],mod-2); per(i,M,1)inv[i-1]=inv[i]*i%mod;
    int n;cin>>n;
    rep(i,1,n)cin>>a[i];
    int k;
    rep(i,1,n){
        if(a[n-i+1]>=i)k=i;
        else break;
    }
    vi aa(k+1);
    rep(i,1,k)aa[i]=a[n-i+1];
    ll ans=cal(k,aa,a[n-k]);
    int v=0;
    rep(i,1,k){
        if(a[n-i+1]>k)++v;
    }
    int r=1;
    rep(i,1,k){
        while(a[r]<i)++r;
        aa[i]=n-r+1;
    }
    ans=(ans+cal(k,aa,v))%mod;
    cout<<k<<' '<<(ans-fac[k]+mod)%mod<<'\n';
}

Details

Tip: Click on the bar to expand more detailed information

Test #1:

score: 100
Accepted
time: 4ms
memory: 27088kb

input:

3
1 2 3

output:

2 6

result:

ok 2 number(s): "2 6"

Test #2:

score: 0
Accepted
time: 8ms
memory: 28372kb

input:

1
1

output:

1 1

result:

ok 2 number(s): "1 1"

Test #3:

score: 0
Accepted
time: 4ms
memory: 27016kb

input:

2
1 1

output:

1 2

result:

ok 2 number(s): "1 2"

Test #4:

score: 0
Accepted
time: 0ms
memory: 28128kb

input:

2
2 2

output:

2 6

result:

ok 2 number(s): "2 6"

Test #5:

score: 0
Accepted
time: 3ms
memory: 28136kb

input:

3
1 1 1

output:

1 3

result:

ok 2 number(s): "1 3"

Test #6:

score: 0
Accepted
time: 3ms
memory: 27196kb

input:

3
2 2 2

output:

2 9

result:

ok 2 number(s): "2 9"

Test #7:

score: 0
Accepted
time: 3ms
memory: 28056kb

input:

3
3 3 3

output:

3 48

result:

ok 2 number(s): "3 48"

Test #8:

score: 0
Accepted
time: 3ms
memory: 28052kb

input:

5
1 1 3 3 4

output:

3 47

result:

ok 2 number(s): "3 47"

Test #9:

score: 0
Accepted
time: 7ms
memory: 28584kb

input:

10
2 4 5 5 5 5 6 8 8 10

output:

5 864

result:

ok 2 number(s): "5 864"

Test #10:

score: 0
Accepted
time: 7ms
memory: 27160kb

input:

30
6 8 9 9 9 10 13 14 15 15 16 17 17 18 20 22 22 23 23 24 24 25 25 25 27 28 28 29 29 30

output:

17 986189864

result:

ok 2 number(s): "17 986189864"

Test #11:

score: 0
Accepted
time: 7ms
memory: 27056kb

input:

123
1 1 1 2 2 3 3 6 6 7 7 7 8 8 9 9 10 10 10 11 12 12 12 13 14 14 14 14 16 17 17 17 17 17 18 19 20 20 21 21 22 22 22 23 23 23 25 25 26 27 27 28 28 28 28 29 29 30 31 31 31 32 33 33 33 34 35 35 35 36 37 37 38 39 39 39 39 40 41 41 42 42 42 43 44 48 48 50 52 53 55 56 57 57 57 58 65 68 71 74 75 76 76 82 ...

output:

42 287179924

result:

ok 2 number(s): "42 287179924"

Test #12:

score: 0
Accepted
time: 7ms
memory: 30204kb

input:

1234
1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 4 5 5 5 5 6 6 7 7 7 7 7 7 7 8 8 8 8 9 9 10 10 10 11 11 11 11 11 12 13 13 14 14 15 15 15 15 16 16 16 17 17 17 18 18 18 19 19 19 19 19 19 19 19 19 19 20 20 20 21 21 21 21 21 22 22 22 23 23 23 23 23 23 23 23 23 24 24 24 24 24 24 24 24 24 24 25 25 25 25 25 26 26 26 26 ...

output:

239 98119841

result:

ok 2 number(s): "239 98119841"

Test #13:

score: 0
Accepted
time: 24ms
memory: 28700kb

input:

2345
1 1 2 2 2 7 7 9 9 9 9 15 17 19 19 22 23 24 25 29 29 29 30 31 32 33 35 37 39 41 42 42 43 43 44 46 46 46 47 48 48 50 51 51 52 53 53 54 55 56 57 58 58 60 61 63 63 64 65 65 65 66 67 67 67 69 69 69 70 71 72 72 73 73 74 75 75 77 77 79 83 85 86 88 90 90 91 93 94 97 99 104 106 107 108 108 109 109 110 1...

output:

1239 588926916

result:

ok 2 number(s): "1239 588926916"

Test #14:

score: 0
Accepted
time: 51ms
memory: 28564kb

input:

3456
4 7 8 8 9 19 20 21 22 23 23 27 29 29 32 32 33 43 45 50 52 52 55 58 58 58 60 62 66 67 68 69 71 74 74 76 77 79 82 82 87 87 88 91 93 95 96 97 99 102 104 106 107 108 121 121 123 126 127 131 137 138 139 142 145 147 152 156 157 159 161 165 166 170 170 172 174 175 178 182 183 185 186 189 190 195 195 1...

output:

2239 24387925

result:

ok 2 number(s): "2239 24387925"

Test #15:

score: 0
Accepted
time: 69ms
memory: 28784kb

input:

4456
4 7 10 10 22 24 29 33 33 34 35 37 40 41 47 48 55 61 61 65 69 71 76 91 95 99 105 105 105 110 112 113 117 117 120 121 122 123 125 127 130 134 135 138 140 141 142 142 144 150 153 154 157 162 165 169 170 170 174 175 176 178 197 198 198 201 208 211 211 212 214 214 215 217 220 224 224 225 230 231 232...

output:

3239 904395650

result:

ok 2 number(s): "3239 904395650"

Test #16:

score: 0
Accepted
time: 97ms
memory: 28124kb

input:

5000
1 5 7 8 24 28 36 47 50 56 59 64 66 85 89 94 95 95 98 108 110 117 122 155 157 158 163 172 172 179 186 197 198 220 236 251 254 254 256 265 287 288 298 302 306 312 327 336 343 344 345 348 350 360 363 364 382 382 390 399 402 406 412 421 425 435 442 445 450 451 453 478 481 490 491 496 499 500 500 50...

output:

4239 328488156

result:

ok 2 number(s): "4239 328488156"

Test #17:

score: 0
Accepted
time: 27ms
memory: 27376kb

input:

5000
5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 5000 ...

output:

5000 317554850

result:

ok 2 number(s): "5000 317554850"

Test #18:

score: -100
Wrong Answer
time: 80ms
memory: 29668kb

input:

5000
4123 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 4999 ...

output:

4999 657596738

result:

wrong answer 2nd numbers differ - expected: '609985488', found: '657596738'