HDU multi-school training first 1012 Sequence

Keywords: PHP

Title Link: acm.hdu.edu.cn/showproblem.php?pid=6589

Topic: Give a n array of length n with m operations and 3 operations of 1,2,3. Ask the array after M operations and output the XOR of i*a[i]

The essence of operation K is to do a n O(n) calculation, a[i]+=a[i-k] (i-k>0)

When k=1, we can see that this is a prefix sum operation

When k=2, we can see that this is a prefix sum operation for 1, 3, 5, 7... 2, 4, 6, 8... subarrays

When k=3, we can see that this is a prefix sum operation for the three subarrays 1, 4, 7, 11...2, 5, 8, 12...3, 6, 9, 12...

The complexity of violence is O(mn). We can simulate the process of violence. In fact, this is not a waste of time, because during the competition, we checked the example of violence and found a nature: the change of operation sequence does not affect the result!

This nature is the key to solving problems. If you do not find this nature, then you cannot expect a positive solution, then the nature of the problem becomes how to quickly find the m-th prefix and sum, which is obviously an o(nm) operation, but not

Observe the process of summing prefixes

0 (not required): a[1], a[2], a[3], a[4], a[5]...

Once: a[1], a[2]+a[1], a[3]+a[2]+a[1], a[4]+a[3]+a[1], a[4]+a[2]+a[1], a[5]+a[4]+a[3]+a[2]+a[1]...

2 times: a[1], a[2]+2a[1], a[3]+2a[2]+3a[1], a[4]+2a[3]+3a[2]+4a[1], a[5]+2a[4]+3a[3]+3a[3]+4a[2]+5a[1]...

3 times: a[1], a[2]+3a[1], a[3]+3a[2]+6a[1], a[4]+3a[3]+6a[2]+10a[1], a[5]+3a[4]+6a[3]+10a[3]+10a[2]+15a[1]...

...

Here, the rule is obvious. We can see that arrays with multiple prefixes and suffixes are related to the number of combinations.

The mth time, the array of combinations should be c[i]=C(m+i-2,i-1), so the above results are expressed as arrays

[1]*a [1]*a [1], c[1]*a [[[[1]]*a [2]]+c[2]*a [[[2]]] [c[1]*a [[[3]]*a [[[3]]+c[2]*a [[2]]*a [[2]] [[[[2]]]*a [1]*a [4]] [[4]] [[[4]*a [1]]]*a [[[[1]]]] [c[1]*a [[[[[[[[[[[[[5]] [[[[[[[5]]]]]]]*a [[[[[[[[2]]]]]*a [[[[[[[[[[a[1]...

This thing is already obvious: the array a[1], a[2], a[3], a[4], a[5]... and b[1], b[2], b[3], b[4], b[5]... convolution results, combination number calculation, O(m) pre-processing, O(1) solution, this is a very classic method, it will not be repeated here, there are a lot of Baidu

There are two methods for convolution: NTT (Fast Arithmetic Transform) and FFT (Fast Fourier Transform). Maybe you don't know them. That's okay. Just set a template. In the case of k=2,k=3, we just need to split the array into subarrays, and then we can change it into k=1 form. So the problem is solved.

It is worth mentioning that since FFT is a complex operation, has floating-point errors, and is a magical operation (no), it is more appropriate to use NTT here. Note a detail, because you use the board several times, you must initialize the data that should be reset inside the board every time you use it.

Especially those two arrays for convolution!!!

By doing a convolution, we get an array of n prefixes and suffixes, the overall time complexity O(m+nlogn)

Top Code:

#include <bits/stdc++.h>
using namespace std;
#define maxn 300005//Note the size of the array used to convolute
#define MOD 998244353
#define mod MOD
#define G 3
typedef long long ll;
namespace NTT {//Template Content
    int rev[maxn], n, m;
    long long A[maxn], B[maxn], C[maxn];

    inline ll Pow(ll a, ll k) {
        ll base = 1;
        while (k) {
            if (k & 1) base = (base * a) % MOD;
            a = (a * a) % MOD;
            k >>= 1;
        }
        return base % MOD;
    }

    void NTT(long long *a, int len, int opt) {
        for (int i = 0; i < len; i++) {
            if (i < rev[i]) {
                swap(a[i], a[rev[i]]);
            }
        }
        for (int i = 1; i < len; i <<= 1) {
            long long wn = Pow(G, (opt * ((MOD - 1) / (i << 1)) + MOD - 1) % (MOD - 1));
            int step = i << 1;
            for (int j = 0; j < len; j += step) {
                long long w = 1;
                for (int k = 0; k < i; k++, w = (1ll * w * wn) % MOD) {
                    long long x = a[j + k];
                    long long y = 1ll * w * a[j + k + i] % MOD;
                    a[j + k] = (x + y) % MOD;
                    a[j + k + i] = (x - y + MOD) % MOD;
                }
            }
        }
        if (opt == -1) {
            long long r = Pow(len, MOD - 2);
            for (int i = 0; i < len; i++)
                a[i] = 1ll * a[i] * r % MOD;
        }
    }

    void solve(int n, int m) {
        int x, l = 0 ,len = 1;
        while (len <= n + m) len <<= 1, ++l;
        for (int i = 0; i < len; ++i)
            rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
        NTT(A, len, 1), NTT(B, len, 1);
        for (int i = 0; i < len; ++i){
            C[i] = (ll) (A[i] * B[i]) % MOD;
            A[i]=B[i]=0;
        }
        NTT(C, len, -1);
    }
}
template <class T>
void read(T &x) {
    static char ch;static bool neg;
    for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
    for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
    x=neg?-x:x;
}
int n,cnt[4];
ll a[100005],c[100005];
ll fac[1000005],inv[1000005];
ll pow_mod(ll m,ll n)
{
    ll res=1;
    while (n)
    {
        if(n&1)res=res*m%mod;
        m=m*m%mod;
        n>>=1;
    }
    return res;
}
void init()
{
    inv[0]=fac[0]=1;
    for(int i=1;i<=1000000;i++)fac[i]=fac[i-1]*i%mod;
    inv[1000000]=pow_mod(fac[1000000],mod-2);
    for(int i=999999;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;
}
ll C(int n,int m)
{
    if(m==0)return 1;//This place is for special handling of the case where cnt=0 is adjusted. At this time the c-array should be 1,0,0,0...
    if(n-m<0)return 0;
    return fac[n]*inv[n-m]%mod*inv[m]%mod;
}
void calc(int m)//A combined array that sums m prefixes
{
    for(int i=1;i<=n;i++){
        c[i]=C(m-2+i,i-1);
    }
}
int main()
{
    init();//Combination Number Preprocessing
    int T;
    cin>>T;
    while (T--)
    {
        memset(cnt,0, sizeof(cnt));
        int m,op;
        read(n);read(m);
        for(int i=1;i<=n;i++)
        {
            read(a[i]);
        }
        for(int i=1;i<=m;i++)
        {
            read(op);
            ++cnt[op];
        }
        calc(cnt[1]);
        for(int i=0;i<n;i++)NTT::A[i]=a[i+1];
        for(int i=0;i<n;i++)NTT::B[i]=c[i+1];
        NTT::solve(n,n);
        for(int i=0;i<n;i++)a[i+1]=NTT::C[i];
        calc(cnt[2]);
        vector<int>d1,d2,d3;
        for(int i=1;i<=n;i++)
        {
            i%2?d1.emplace_back(a[i]):d2.emplace_back(a[i]);
        }
        for(int i=0;i<d1.size();i++)NTT::A[i]=d1[i];
        for(int i=0;i<d1.size();i++)NTT::B[i]=c[i+1];
        NTT::solve(d1.size(),d1.size());
        for(int i=1;i<=n;i+=2)a[i]=NTT::C[i/2];
        for(int i=0;i<d2.size();i++)NTT::A[i]=d2[i];
        for(int i=0;i<d2.size();i++)NTT::B[i]=c[i+1];
        NTT::solve(d2.size(),d2.size());
        for(int i=2;i<=n;i+=2)a[i]=NTT::C[i/2-1];
        d1.clear();
        d2.clear();
        calc(cnt[3]);
        for(int i=1;i<=n;i++)
        {
            if(i%3==1)d1.emplace_back(a[i]);
            else if(i%3==2)d2.emplace_back(a[i]);
            else d3.emplace_back(a[i]);
        }
        for(int i=0;i<d1.size();i++)NTT::A[i]=d1[i];
        for(int i=0;i<d1.size();i++)NTT::B[i]=c[i+1];
        NTT::solve(d1.size(),d1.size());
        for(int i=1;i<=n;i+=3)a[i]=NTT::C[i/3];
        for(int i=0;i<d2.size();i++)NTT::A[i]=d2[i];
        for(int i=0;i<d2.size();i++)NTT::B[i]=c[i+1];
        NTT::solve(d2.size(),d2.size());
        for(int i=2;i<=n;i+=3)a[i]=NTT::C[i/3];
        for(int i=0;i<d3.size();i++)NTT::A[i]=d3[i];
        for(int i=0;i<d3.size();i++)NTT::B[i]=c[i+1];
        NTT::solve(d3.size(),d3.size());
        for(int i=3;i<=n;i+=3)a[i]=NTT::C[i/3-1];
        ll ans=0;
        for(int i=1;i<=n;i++)ans=ans^(1ll*i*a[i]);
        cout<<ans<<endl;
    }
    return 0;
}

Scale gives us a better idea. When k=2, we turn the C array of k=1 into c[1], 0, c[2], 0, c[3], 0....

When k=3, it becomes c[1], 0, 0, c[2], 0, 0, c[3], 0, 0... and convolutes the two arrays directly.

This is the code written according to the idea of the scale, which is much simpler and has a smaller constant.

#include <bits/stdc++.h>
using namespace std;
#define maxn 300005
#define MOD 998244353
#define mod MOD
#define G 3
typedef long long ll;
int rev[maxn];
long long C[maxn];

inline ll Pow(ll a, ll k) {
    ll base = 1;
    while (k) {
        if (k & 1) base = (base * a) % MOD;
        a = (a * a) % MOD;
        k >>= 1;
    }
    return base % MOD;
}

void NTT(long long *a, int len, int opt) {
    for (int i = 0; i < len; ++i) {
        if (i < rev[i]) {
            swap(a[i], a[rev[i]]);
        }
    }
    for (int i = 1; i < len; i <<= 1) {
        long long wn = Pow(G, (opt * ((MOD - 1) / (i << 1)) + MOD - 1) % (MOD - 1));
        int step = i << 1;
        for (int j = 0; j < len; j += step) {
            long long w = 1;
            for (int k = 0; k < i; ++k, w = (1ll * w * wn) % MOD) {
                long long x = a[j + k];
                long long y = 1ll * w * a[j + k + i] % MOD;
                a[j + k] = (x + y) % MOD;
                a[j + k + i] = (x - y + MOD) % MOD;
            }
        }
    }
    if (opt == -1) {
        long long r = Pow(len, MOD - 2);
        for (int i = 0; i < len; i++)
            a[i] = 1ll * a[i] * r % MOD;
    }
}

void solve(ll A[],ll B[],int n, int m) {
    int x, l = 0 ,len = 1;
    while (len <= n + m) len <<= 1, ++l;
    for (int i = 0; i < len; ++i)
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
    NTT(A, len, 1), NTT(B, len, 1);
    for (int i = 0; i < len; ++i) {
        C[i] = (ll) (A[i] * B[i]) % MOD;
        A[i] = B[i] = 0;
    }
    NTT(C, len, -1);
}
void read(ll &x) {
    static char ch;static bool neg;
    for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
    for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
    x=neg?-x:x;
}
int n,cnt[4];
ll a[maxn],c[maxn];
ll fac[1000005],inv[1000005];
ll pow_mod(ll m,ll n)
{
    ll res=1;
    while (n)
    {
        if(n&1)res=res*m%mod;
        m=m*m%mod;
        n>>=1;
    }
    return res;
}
void init()
{
    inv[0]=fac[0]=1;
    for(int i=1;i<=1000000;i++)fac[i]=fac[i-1]*i%mod;
    inv[1000000]=pow_mod(fac[1000000],mod-2);
    for(int i=999999;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;
}
ll Comb(int n,int m)
{
    return n<m?0:fac[n]*inv[n-m]%mod*inv[m]%mod;
}
int main()
{
    init();//Combination Number Preprocessing
    int T;
    cin>>T;
    while (T--)
    {
        memset(cnt,0, sizeof(cnt));
        ll m,op;
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            read(a[i]);
        }
        for(int i=1;i<=m;i++)
        {
            read(op);
            ++cnt[op];
        }
        for(int i=1;i<=3;i++)
        {
            memset(c,0, sizeof(c));
            for(int j=0;j*i<n;j++)
            {
                c[j*i]=Comb(cnt[i]-1+j,j);
            }
            if(cnt[i]==0)c[0]=1;//Special handling
            solve(a+1,c,n,n);
            for(int i=0;i<n;i++)a[i+1]=C[i];
        }
        ll ans=0;
        for(int i=1;i<=n;i++)ans=ans^(1ll*i*a[i]);
        cout<<ans<<endl;
    }
    return 0;
}

Summary: This question is not difficult on the whole. Although the process is cumbersome and FFT was not written out during the competition, the total gains are still great. Previously, there was no way to solve the few questions that have been used in this kind of competition. Now you can also analyze the seven or eight by yourself, which is a kind of progress.

Think about it, don't give up easily, maybe you can harvest AC in the next moment!

Posted by omegared on Mon, 22 Jul 2019 13:40:58 -0700