返回顶部

The 2019 Asia Nanchang First Round Online Programming Contest

目录

D Interesting Series
E Magic Master
I Yukino With Subinterval

D Interesting Series

在dq大佬的指点下搞出来的,貌似因为w的误差累积导致要longdouble才能通过。当然据说是预处理w也可以通过。

首先把题目的Chinglish翻译成中文,容易发现: \(F_n=1+a+a^2+...+a^{n-1}=\frac{a^n-1}{a-1}\) ,然后每次询问的k,就对应从题目给的一堆数里面选出恰好k个,每种选法的数的和记为sum,那么就把所有的 \(F_{sum}\) 求和。

记答案为: \(Ans_k=\sum\limits_{|s|==k}F_{sum}\) 其中s遍历所有子集,而sum就是集合s的数字的和。那么: \(Ans_k=\sum\limits_{|s|==k}F_{sum}=\sum\limits_{|s|==k}\frac{a^{sum}-1}{a-1}=\frac{1}{a-1}\sum\limits_{|s|==k}(a^{sum}-1)=\frac{1}{a-1}(\sum\limits_{|s|==k}(a^{sum})-C_n^k)\)

为了方便记: \(G_k=\sum\limits_{|s|==k}a^{sum}\) 考虑这个东西怎么求。

那么很显然的:生成函数: \((x+a^{s_1})*(x+a^{s_2})*...*(x+a^{s_n})\) 之中任意选k个第二项的所有选法就遍历了所有的 \(a^{sum}\) ,那么 \(G_k\) 就是左边那个连乘积的其中\(x^{n-k}\)这个项的系数,因为这个恰好选了k个第二项。用分治FFT快速求出这个系数。

下面的代码中,多项式的排列顺序是按x的次数的升次排序。需要注意的是这里是每一步都要把点值变回系数才行(貌似是因为他们并不是同一个n所以不能直接一起算否则复杂度更不对),而且每一步变回多项式的时候就可以取整然后取模了,不要等误差累计之后才取整,更不要等精度损失到没有之后才取模。

哈夫曼分治,空间花费貌似比较大,可能是因为没有进行回收的原因,但是可以更好地应付不同项数的多项式:进行及时回收之后效果非常不错,定义了默认构造函数之后可以resize加值非常方便。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define double long double

const double PI = acos(-1.0);
const int MOD = 100003;
const int MAXN = 1e5;

int qpow(ll x, int n) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % MOD;
        x = x * x % MOD;
        n >>= 1;
    }
    return res;
}

int inva_1;

ll ans[MAXN + 5];

ll inv[MAXN + 5], fac[MAXN + 5], invfac[MAXN + 5];

void init(int n) {
    inv[1] = 1;
    for(int i = 2; i <= n; i++) {
        inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD;
    }
    fac[0] = 1, invfac[0] = 1;
    for(int i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i % MOD;
        invfac[i] = invfac[i - 1] * inv[i] % MOD;
    }
}

inline ll C(ll n, ll m) {
    if(n < m)
        return 0;
    return fac[n] * invfac[n - m] % MOD * invfac[m] % MOD;
}


struct Complex {
    double x, y;
    Complex(): x(0), y(0) {}
    Complex(double x, double y): x(x), y(y) {}
    friend Complex operator+(const Complex &a, const Complex &b) {
        return Complex(a.x + b.x, a.y + b.y);
    }
    friend Complex operator-(const Complex &a, const Complex &b) {
        return Complex(a.x - b.x, a.y - b.y);
    }
    friend Complex operator*(const Complex &a, const Complex &b) {
        return Complex(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
    }
};

typedef vector<Complex> Poly;

void FFT(Poly &a, int n, int op) {
    for(int i = 1, j = n >> 1; i < n - 1; ++i) {
        if(i < j)
            swap(a[i], a[j]);
        int k = n >> 1;
        while(k <= j) {
            j -= k;
            k >>= 1;
        }
        j += k;
    }
    for(int len = 2; len <= n; len <<= 1) {
        Complex wn(cos(2.0 * PI / len), sin(2.0 * PI / len)*op);
        for(int i = 0; i < n; i += len) {
            Complex w(1.0, 0.0);
            for(int j = i; j < i + (len >> 1); ++j) {
                Complex u = a[j], t = a[j + (len >> 1)] * w ;
                a[j] = u + t, a[j + (len >> 1)] = u - t;
                w = w * wn;
            }
        }
    }
    if(op == -1) {
        for(int i = 0; i < n; ++i)
            a[i].x = (ll)(a[i].x / n + 0.5);
    }
}

int pow2(int x) {
    int res = 1;
    while(res < x)
        res <<= 1;
    return res;
}

int convolution(Poly &A, Poly &B, int Asize, int Bsize) {
    int n = pow2(Asize + Bsize - 1);
    A.resize(n);
    B.resize(n);
    FFT(A, n, 1);
    FFT(B, n, 1);
    for(int i = 0; i < n; ++i)
        A[i] = A[i] * B[i];
    FFT(A, n, -1);
    for(int i = 0; i < n; ++i) {
        A[i].x = ((ll)A[i].x) % MOD;
        A[i].y = 0.0;
    }
    while(n && (A[n - 1].x == 0))
        --n;
    return n;
}

Poly poly[MAXN + 5];

priority_queue<pair<int, int> > pq;

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    init(100000);
    int n, a, q;
    scanf("%d%d%d", &n, &a, &q);
    inva_1 = qpow((a - 1 + MOD) % MOD, MOD - 2);
    for(int i = 1; i <= n; ++i) {
        int x;
        scanf("%d", &x);
        poly[i].push_back(Complex(1, 0));
        poly[i].push_back(Complex(qpow(a, x), 0));
        pq.push({-2, i});
    }
    while(pq.size() >= 2) {
        int id = pq.top().second;
        Poly &A = poly[pq.top().second];
        int Asize = -(pq.top().first);
        pq.pop();
        Poly &B = poly[pq.top().second];
        int Bsize = -(pq.top().first);
        pq.pop();
        int Csize = convolution(A, B, Asize, Bsize);
        Poly().swap(B);
        pq.push({-Csize, id});
    }
    Poly &A = poly[pq.top().second];
    int Asize = -(pq.top().first);
    for(int i = 0; i <= n; ++i)
        ans[i] = ((ll)A[i].x - C(n, i) + MOD) % MOD * inva_1 % MOD;
    while(q--) {
        int k;
        scanf("%d", &k);
        printf("%lld\n", ans[k]);
    }
    return 0;
}

二分治:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define double long double

const int MOD = 100003;
const double PI = acos(-1.0);

struct Complex {
    double x, y;
    Complex() {x = 0.0, y = 0.0;}
    Complex(double x, double y): x(x), y(y) {}
    friend Complex operator+(const Complex &a, const Complex &b) {
        return Complex(a.x + b.x, a.y + b.y);
    }
    friend Complex operator-(const Complex &a, const Complex &b) {
        return Complex(a.x - b.x, a.y - b.y);
    }
    friend Complex operator*(const Complex &a, const Complex &b) {
        return Complex(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
    }
};

typedef vector<Complex> Poly;

inline void FFT(Poly &a, int n, int op) {
    for(int i = 1, j = n >> 1; i < n - 1; ++i) {
        if(i < j)
            swap(a[i], a[j]);
        int k = n >> 1;
        while(k <= j) {
            j -= k;
            k >>= 1;
        }
        j += k;
    }
    for(int len = 2; len <= n; len <<= 1) {
        Complex wn(cos(2.0 * PI / len), sin(2.0 * PI / len)*op);
        for(int i = 0; i < n; i += len) {
            Complex w(1.0, 0.0);
            for(int j = i; j < i + (len >> 1); ++j) {
                Complex u = a[j], t = a[j + (len >> 1)] * w ;
                a[j] = u + t, a[j + (len >> 1)] = u - t;
                w = w * wn;
            }
        }
    }
    if(op == -1) {
        for(int i = 0; i < n; ++i) {
            a[i].x = ((ll)(a[i].x / n + 0.5)) % MOD;
            a[i].y = 0;
        }
    }
}

inline int pow2(int x) {
    int res = 1;
    while(res < x)
        res <<= 1;
    return res;
}


inline int convolution(Poly &A, Poly &B, int Asize, int Bsize) {
    int n = pow2(Asize + Bsize - 1);
    A.resize(n);
    B.resize(n);
    FFT(A, n, 1);
    FFT(B, n, 1);
    for(int i = 0; i < n; ++i)
        A[i] = A[i] * B[i];
    FFT(A, n, -1);
    return n;
}

const int MAXN = 1e5;

int qpow(ll x, int n) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % MOD;
        x = x * x % MOD;
        n >>= 1;
    }
    return res;
}

int inva_1;

ll ans[MAXN + 5];

ll inv[MAXN + 5], fac[MAXN + 5], invfac[MAXN + 5];

inline void init(int n) {
    inv[1] = 1;
    for(int i = 2; i <= n; i++) {
        inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD;
    }
    fac[0] = 1, invfac[0] = 1;
    for(int i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i % MOD;
        invfac[i] = invfac[i - 1] * inv[i] % MOD;
    }
}

inline ll C(ll n, ll m) {
    if(n < m)
        return 0;
    return fac[n] * invfac[n - m] % MOD * invfac[m] % MOD;
}

int x[MAXN + 5];
Poly Solve(int l, int r) {
    if(l == r) {
        Poly poly;
        poly.push_back(Complex(1, 0));
        poly.push_back(Complex(x[l], 0));
        return poly;
    }
    int mid = l + r >> 1;
    Poly A = Solve(l, mid);
    Poly B = Solve(mid + 1, r);
    /*int Asize=A.size();
    for(int i = 0; i < Asize; ++i) {
        printf(" %d", (int)A[i].x);
    }
    printf("\n*\n");
    int Bsize=B.size();
    for(int i = 0; i < Bsize; ++i) {
        printf(" %d", (int)B[i].x);
    }
    printf("\n=\n");*/
    int Csize = convolution(A, B, A.size(), B.size());
    /*for(int i = 0; i < Csize; ++i) {
        printf(" %d", (int)A[i].x);
    }
    printf("\n\n");*/
    return A;
}

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    init(100000);
    int n, a, q;
    scanf("%d%d%d", &n, &a, &q);
    inva_1 = qpow((a - 1 + MOD) % MOD, MOD - 2);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &x[i]);
        x[i] = qpow(a, x[i]);
    }
    Poly A = Solve(1, n);
    for(int i = 0; i <= n; ++i)
        ans[i] = ((ll)A[i].x - C(n, i) + MOD) % MOD * inva_1 % MOD;
    while(q--) {
        int k;
        scanf("%d", &k);
        printf("%lld\n", ans[k]);
    }
    return 0;
}

在给哈夫曼分治加入回收之后貌似不仅更省内存还更快,迷惑,其实只需要加入:Poly ().swap(B); ,然后B就会被回收掉了。

最快的版本也相对比较省内存的就这个了,通过最高精度预处理w,然后跑得飞快,double就足够过了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define double long double

const double PI = acos(-1.0);
const int MOD = 100003;
const int MAXN = 1e5;

inline int qpow(ll x, int n) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % MOD;
        x = x * x % MOD;
        n >>= 1;
    }
    return res;
}

int inva_1;

ll ans[MAXN + 5];

ll inv[MAXN + 5], fac[MAXN + 5], invfac[MAXN + 5];

void init(int n) {
    inv[1] = 1;
    for(int i = 2; i <= n; i++) {
        inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD;
    }
    fac[0] = 1, invfac[0] = 1;
    for(int i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i % MOD;
        invfac[i] = invfac[i - 1] * inv[i] % MOD;
    }
}

inline ll C(ll n, ll m) {
    if(n < m)
        return 0;
    return fac[n] * invfac[n - m] % MOD * invfac[m] % MOD;
}

struct Complex {
    double x, y;
    Complex(): x(0), y(0) {}
    Complex(double x, double y): x(x), y(y) {}
    friend Complex operator+(const Complex &a, const Complex &b) {
        return Complex(a.x + b.x, a.y + b.y);
    }
    friend Complex operator-(const Complex &a, const Complex &b) {
        return Complex(a.x - b.x, a.y - b.y);
    }
    friend Complex operator*(const Complex &a, const Complex &b) {
        return Complex(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
    }
};

typedef vector<Complex> Poly;

Poly w[2 * MAXN + 5][2];
int rev[4 * MAXN + 5];

inline void FFT(Poly &a, int n, int op) {
    for(int i = 0; i < n ; ++i) {
        if(i < rev[i])
            swap(a[i], a[rev[i]]);
    }
    for(int len = 2; len <= n; len <<= 1) {
        register int m = len >> 1;
        for(int i = 0; i < m; ++i) {
            Complex &tw = w[m][op == 1][i];
            for(int j = i; j < n; j += len) {
                Complex u = a[j], t = a[j + m] * tw ;
                a[j] = u + t, a[j + m] = u - t;
            }
        }
    }
    if(op == -1) {
        for(int i = 0; i < n; ++i) {
            a[i].x = ((ll)(a[i].x / n + 0.5)) % MOD;
            a[i].y = 0;
        }
    }
}

inline int pow2(int x, int &lgn) {
    int res = 1;
    lgn = 0;
    while(res < x) {
        if(w[res][0].size() == 0) {
            w[res][0].resize(res);
            w[res][1].resize(res);
            for(int i = 0; i < res; ++i) {
                //0是逆变换需要的
                w[res][0][i] = Complex(cos(-PI * i  / res), sin(-PI * i / res));
                w[res][1][i] = Complex(cos(PI * i  / res), sin(PI * i  / res));
            }
        }
        res <<= 1;
        ++lgn;
    }
    return res;
}

inline int convolution(Poly &A, Poly &B, int Asize, int Bsize) {
    int lgn, n = pow2(Asize + Bsize - 1, lgn);
    for(int i = 0; i < n; ++i)
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << lgn - 1);
    A.resize(n);
    B.resize(n);
    FFT(A, n, 1);
    FFT(B, n, 1);
    for(int i = 0; i < n; ++i)
        A[i] = A[i] * B[i];
    FFT(A, n, -1);
    while(n && (A[n - 1].x == 0))
        --n;
    return n;
}

Poly poly[MAXN + 5];

priority_queue<pair<int, int> > pq;

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    init(100000);
    int n, a, q;
    scanf("%d%d%d", &n, &a, &q);
    inva_1 = qpow((a - 1 + MOD) % MOD, MOD - 2);
    for(int i = 1; i <= n; ++i) {
        int x;
        scanf("%d", &x);
        poly[i].push_back(Complex(1, 0));
        poly[i].push_back(Complex(qpow(a, x), 0));
        pq.push({-2, i});
    }

    while(pq.size() >= 2) {
        int id = pq.top().second;
        Poly &A = poly[pq.top().second];
        int Asize = -(pq.top().first);
        pq.pop();
        Poly &B = poly[pq.top().second];
        int Bsize = -(pq.top().first);
        pq.pop();
        int Csize = convolution(A, B, Asize, Bsize);
        Poly().swap(B);
        pq.push({-Csize, id});
    }
    Poly &A = poly[pq.top().second];
    int Asize = -(pq.top().first);
    for(int i = 0; i <= n; ++i)
        ans[i] = ((ll)A[i].x - C(n, i) + MOD) % MOD * inva_1 % MOD;
    while(q--) {
        int k;
        scanf("%d", &k);
        printf("%lld\n", ans[k]);
    }
    return 0;
}

E Magic Master

比赛的时候xy用链表弄了一个,赛后发现deque更快,可能原因是deque内存中连续。总之deque是一种高效的数据结构,甚至可以随机访问随机删除,自动管理的分块数组?只不过不能手动在块上维护信息就是麻烦点。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

deque<int> dq;

int main() {
    int T;
    scanf("%d", &T);
    for(int ti = 1; ti <= T; ++ti) {
        int n, m;
        scanf("%d%d", &n, &m);
        dq.push_back(n);
        for(int i = n - 1; i >= 1; --i) {
            for(int mi = 1; mi <= m; ++mi) {
                dq.push_back(dq.front());
                dq.pop_front();
            }
            dq.push_back(i);
        }

        int Q;
        scanf("%d", &Q);
        for(int qi = 1, q; qi <= Q; ++qi) {
            scanf("%d", &q);
            printf("%d\n", dq[n - q]);
        }
        dq.clear();
    }
    return 0;
}

B. Fire-Fighting Hero

理解错题意了,出题方有点东西。在新的题意背景下所有的消防队是同一个单源出发的,只需要找一个超级源连一条权为0的边即可。

A. Enju With math problem

先暴力判前200个质数防止后面出些小东西。

看起来是要先拉出1.5e8的那几个为数不多的质数,检测一下他们周围是不是有,花的复杂度不高。然后把所有这些质数乘以2得到一些合数2p,那么(除了22=4以外)根据欧拉函数的积性性质,他们的欧拉函数一定是phi(2p)=phi(2)phi(p),根据这些合数定位再找一遍。

根据下面这个验证程序可以知道,当进行了多次筛之后可以保证每个区间都至少包含一个这样的数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN = 1e8 + 5e7;
bool np[MAXN + 5];
bool _2p[MAXN + 5], _3p[MAXN + 5];
bool _5p[MAXN + 5], _7p[MAXN + 5];
bool _11p[MAXN + 5], _17p[MAXN + 5];
int p[8444396 + 5], ptop;
void sieve() {
    np[1] = 1;
    ptop = 0;
    for(int i = 2; i <= MAXN; ++i) {
        if(!np[i]) {
            p[++ptop] = i;
        }
        for(int j = 1; j <= ptop; ++j) {
            ll t = i * p[j];
            if(t > MAXN)
                break;
            np[t] = 1;
            if(i % p[j] == 0)
                break;
        }
    }
    //printf("ptop=%d\n",ptop);
    for(int i = 1; i <= ptop; ++i) {
        if(2ll * p[i] > MAXN)
            break;
        _2p[p[i] * 2] = 1;
    }
    for(int i = 1; i <= ptop; ++i) {
        if(3ll * p[i] > MAXN)
            break;
        _3p[p[i] * 3] = 1;
    }
    for(int i = 1; i <= ptop; ++i) {
        if(5ll * p[i] > MAXN)
            break;
        _5p[p[i] * 5] = 1;
    }
    for(int i = 1; i <= ptop; ++i) {
        if(7ll * p[i] > MAXN)
            break;
        _7p[p[i] * 7] = 1;
    }
    for(int i = 1; i <= ptop; ++i) {
        if(11ll * p[i] > MAXN)
            break;
        _11p[p[i] * 11] = 1;
    }
    for(int i = 1; i <= ptop; ++i) {
        if(17ll * p[i] > MAXN)
            break;
        _17p[p[i] * 17] = 1;
    }
    int maxdis = 0, pre = 2;
    for(int i = 3; i <= MAXN; ++i) {
        if((!np[i]) || _2p[i] || _3p[i] || _5p[i] || _7p[i] || _11p[i] || _17p[i]) {
            maxdis = max(maxdis, i - pre);
            pre = i;
        }
    }
    printf("maxdis=%d\n", maxdis);
}

int main() {
    sieve();

    return 0;
}

C. Hello 2019

这个看起来就像早上的wrong那道题。

I Yukino With Subinterval

一开始考虑以为要差分,或者像主席树那样弄但是感觉维护起来很复杂。看了别人的示范使用CDQ分治的,思路大概就是:

首先题目把连续的一段相等的(并且在此基础上最长)数列视作一段,就是要求某个[l,r]区间里面值也在[x,y]里面的数列有多少段。

设一个辅助数列b[i]=(a[i]==a[i-1])?0:a[i],这样每段数字只有第一个有效了(题目询问的x最少是1,所以设b[i]为0就不会被询问到)。维护a序列把更新操作写到a序列上,会发现最多会影响b[i]和b[i+1]两个点,那么b数列也是可以维护的了。观察原来的问题,在CDQ分治的角度下是静态的问题,给一个区间[1,n],里面撒一些点,点的y坐标就是这个b[i]的值,那么每次就是问一个矩形区域里面的点的数量,和前面几天网赛的那个处理倍数变成二维偏序是一个意思。把一个[l,r][x,y]的询问拆成[1,l-1][x,y]和[1,r][x,y],那么对这个区间内的点从左到右进行插入就只需要在树状数组上维护[x,y]。

下面是一些细节:我们取了一段连续数列的第一个元素代表它,那么需要转换回题目的问题就需要特殊考虑左端点,方法是原本是把一个[l,r][x,y]的询问拆成[1,l-1][x,y]和[1,r][x,y],但现在是变成把一个[l+1,r][x,y]的询问拆成[1,l][x,y]和[1,r][x,y],然后l点在维护a数列的时候就直接特判累计答案。然后修改操作和上次天使玩偶那种添加操作不一样,这个修改操作要先把原本的影响消除。最后因为b数组是存在0的,要把整体偏移一个位置。

复杂度感觉玄学,毕竟把一个操作裂成4个,但是加上剪枝还是可以的。最终成绩大概800ms以内,64MB。

注意因为实际上分裂了4个操作所以假如把一开始的点也考虑进query的话要开5倍空间。假如空间紧迫的话,首先把初始点全部插进去就不保存了,然后宁愿压掉t和op也不要从归并那里压。其次既然修改已经裂成4个了那么这个查询也可以裂成4个,这样就少了d2。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

namespace FastIO {
#define BUF_SIZE 1000000
    bool IOError = 0;
    inline char NextChar() {
        static char buf[BUF_SIZE], *pl = buf + BUF_SIZE, *pr = buf + BUF_SIZE;
        if(pl == pr) {
            pl = buf, pr = buf + fread(buf, 1, BUF_SIZE, stdin);
            if(pr == pl) {
                IOError = 1;
                return -1;
            }
        }
        return *pl++;
    }
#undef BUF_SIZE

    inline bool Blank(char c) {
        return c == ' ' || c == '\n' || c == '\r' || c == '\t';
    }

    template<class T> inline void Read(T &x) {
        char c;
        while(Blank(c = NextChar()));
        if(!IOError) {
            for(x = 0; '0' <= c && c <= '9'; c = NextChar())
                x = (x << 3) + (x << 1) + c - '0';
        }
    }

    template<class T> inline void PutChar(T x) {
        if(x > 9)
            PutChar(x / 10);
        putchar(x % 10 + '0');
    }

    template<class T> inline void Write(T &x) {
        PutChar(x);
        putchar('\n');
    }

}

using namespace FastIO;

const int MAXN = 2e5;

//因为题目的数据有0,所以全部+1偏移了一位
const int H = 2e5 + 1;
int bit[H + 5];

inline void clear() {
    memset(bit, 0, sizeof(bit));
}

inline void add(int x, int v) {
    for(; x <= H; x += x & -x) {
        bit[x] += v;
    }
}

inline void set0(int x) {
    for(; x <= H; x += x & -x) {
        bit[x] = 0;
    }
}

inline int sum(int x) {
    int res = 0;
    for(; x ; x -= x & -x) {
        res += bit[x];
    }
    return res;
}

struct Query {
    int t, op, x, d1, d2;
    bool operator<(const Query &q)const {
        return x < q.x;
    }
} q[5 * MAXN + 5], tq[5 * MAXN + 5], tq2[5 * MAXN + 5];

inline int before_calc(const int &l, const int &r, const int &mid) {
    int l1 = 0;
    for(int i = l; i <= mid; ++i) {
        if(q[i].op == 1)
            tq[++l1] = q[i];
    }
    int l2 = l1;
    for(int i = mid + 1; i <= r; ++i) {
        if(q[i].op != 1)
            tq[++l2] = q[i];
    }
    merge(tq + 1, tq + 1 + l1, tq + 1 + l1, tq + 1 + l2, tq2 + 1);
    for(int j = 1; j <= l2; ++j)
        tq[j] = tq2[j];
    return l2;
}

inline void after_calc(const int &l, const int &r, const int &mid) {
    merge(q + l, q + mid + 1, q + mid + 1, q + r + 1, tq2 + 1);
    for(int j = l; j <= r; ++j)
        q[j] = tq2[j - l + 1];
}

//超过该界时不进行取消树状数组,直接重建,因为树状数组有很多加法、判断之类的,而memset快得多
const int BUILDLIMIT = 5e4;

int ans[MAXN + 5];
bool vis[MAXN + 5];

//返回更新操作进行的次数
inline int calc(const int &tqtop) {
    int cnt = 0;
    for(int i = 1, nxt; i <= tqtop; i = nxt) {
        for(nxt = i + 1; nxt <= tqtop && tq[i].x == tq[nxt].x; ++nxt);
        for(int j = i; j < nxt; ++j) {
            if(tq[j].op == 1) {
                add(tq[j].d1, tq[j].d2);
                ++cnt;
            }
        }
        for(int j = i; j < nxt; ++j) {
            if(tq[j].op == 2) {
                //前面的转换已经把l可能产生的贡献已经清除了
                ans[tq[j].t] -= sum(tq[j].d2) - sum(tq[j].d1);
            } else if(tq[j].op == 3) {
                ans[tq[j].t] += sum(tq[j].d2) - sum(tq[j].d1);
                //op2和op3都是成对出现的,只对其中一个赋值就可以了
                vis[tq[j].t] = true;
            }
        }
    }
    return cnt;
}

int n, m;

void solve(const int &l, const int &r, bool firstlayer = false) {
    if(l == r)
        return;
    if(!firstlayer) {
        bool all1 = true;
        for(int i = l; i <= r ; i++) {
            if(tq[i].op != 1) {
                all1 = false;
                break;
            }
        }
        if(all1) {
            sort(q + l, q + r + 1);
            return;
        }
    }
    int mid = (l + r) >> 1;
    solve(l, mid);
    solve(mid + 1, r);
    int tqtop = before_calc(l, r, mid);
    int cnt = calc(tqtop);
    if(!firstlayer) {
        if(cnt < BUILDLIMIT) {
            for(int j = 1; j <= tqtop; ++j) {
                if(tq[j].op == 1)
                    set0(tq[j].d1);
            }
        } else
            clear();
        after_calc(l, r, mid);
    }
    return;
}

int a[MAXN + 5], b[MAXN + 5];

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    Read(n), Read(m);
    int qtop = 0;

    for(int i = 1; i <= n; ++i) {
        Read(a[i]);
        b[i] = (a[i] == a[i - 1]) ? 0 : a[i];

        q[++qtop].t = 0;
        q[qtop].op = 1;
        q[qtop].x = i;
        q[qtop].d1 = b[i] + 1;
        q[qtop].d2 = +1;
    }

    for(int i = 1, op; i <= m; ++i) {
        Read(op);
        if(op == 1) {
            int x, y;
            Read(x), Read(y);
            q[++qtop].t = i;
            q[qtop].op = 1;
            q[qtop].x = x;
            q[qtop].d1 = b[x] + 1;
            q[qtop].d2 = -1;

            q[++qtop].t = i;
            q[qtop].op = 1;
            q[qtop].x = x + 1;
            q[qtop].d1 = b[x + 1] + 1;
            q[qtop].d2 = -1;

            a[x] = y;
            b[x] = (a[x] == a[x - 1]) ? 0 : a[x];
            b[x + 1] = (a[x + 1] == a[x]) ? 0 : a[x + 1];

            q[++qtop].t = i;
            q[qtop].op = 1;
            q[qtop].x = x;
            q[qtop].d1 = b[x] + 1;
            q[qtop].d2 = +1;

            q[++qtop].t = i;
            q[qtop].op = 1;
            q[qtop].x = x + 1;
            q[qtop].d1 = b[x + 1] + 1;
            q[qtop].d2 = +1;

        } else {
            int l, r, x, y;
            Read(l), Read(r), Read(x), Read(y);
            //op2是要被减去的,op3是要被加上的
            q[++qtop].t = i;
            q[qtop].op = 2;
            //这里询问的就是要l而不是l-1,为了把l可能产生的贡献去掉然后加回来
            q[qtop].x = l;
            q[qtop].d1 = x - 1 + 1;
            q[qtop].d2 = y + 1;

            q[++qtop].t = i;
            q[qtop].op = 3;
            q[qtop].x = r;
            q[qtop].d1 = x - 1 + 1;
            q[qtop].d2 = y + 1;

            //把l的贡献加回来
            if(x <= a[l] && a[l] <= y)
                ++ans[i];
        }
    }

    solve(1, qtop, true);
    for(int i = 1; i <= m; ++i) {
        if(vis[i])
            Write(ans[i]);
    }
}

进行一些卡常,因为merge的时候是跟实际移动的数据有关系的,所以把t和op合在一起说不定会更快,这个是620ms然后39MB的解法,时间和空间都比较节约。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

namespace FastIO {
#define BUF_SIZE 1000000
    bool IOError = 0;
    inline char NextChar() {
        static char buf[BUF_SIZE], *pl = buf + BUF_SIZE, *pr = buf + BUF_SIZE;
        if(pl == pr) {
            pl = buf, pr = buf + fread(buf, 1, BUF_SIZE, stdin);
            if(pr == pl) {
                IOError = 1;
                return -1;
            }
        }
        return *pl++;
    }
#undef BUF_SIZE

    inline bool Blank(char c) {
        return c == ' ' || c == '\n' || c == '\r' || c == '\t';
    }

    template<class T> inline void Read(T &x) {
        char c;
        while(Blank(c = NextChar()));
        if(!IOError) {
            for(x = 0; '0' <= c && c <= '9'; c = NextChar())
                x = (x << 3) + (x << 1) + c - '0';
        }
    }

    template<class T> inline void PutChar(T x) {
        if(x > 9)
            PutChar(x / 10);
        putchar(x % 10 + '0');
    }

    template<class T> inline void Write(T &x) {
        PutChar(x);
        putchar('\n');
    }

}

using namespace FastIO;

const int MAXN = 2e5;

//因为题目的数据有0,所以全部+1偏移了一位
const int H = 2e5 + 1;
int bit[H + 5];

inline void clear() {
    memset(bit, 0, sizeof(bit));
}

inline void add(int x, int v) {
    for(; x <= H; x += x & -x) {
        bit[x] += v;
    }
}

inline void set0(int x) {
    for(; x <= H; x += x & -x) {
        bit[x] = 0;
    }
}

inline int sum(int x) {
    int res = 0;
    for(; x ; x -= x & -x) {
        res += bit[x];
    }
    return res;
}

struct Query {
    int top, x, d1;
    bool operator<(const Query &q)const {
        return x < q.x;
    }
} q[5 * MAXN + 5], tq[5 * MAXN + 5], tq2[5 * MAXN + 5];

inline int before_calc(const int &l, const int &r, const int &mid) {
    int l1 = 0;
    for(int i = l; i <= mid; ++i) {
        if((q[i].top & 3) <= 1)
            tq[++l1] = q[i];
    }
    int l2 = l1;
    for(int i = mid + 1; i <= r; ++i) {
        if((q[i].top & 3) >= 2)
            tq[++l2] = q[i];
    }
    merge(tq + 1, tq + 1 + l1, tq + 1 + l1, tq + 1 + l2, tq2 + 1);
    for(int j = 1; j <= l2; ++j)
        tq[j] = tq2[j];
    return l2;
}

inline void after_calc(const int &l, const int &r, const int &mid) {
    merge(q + l, q + mid + 1, q + mid + 1, q + r + 1, tq2 + 1);
    for(int j = l; j <= r; ++j)
        q[j] = tq2[j - l + 1];
}

//超过该界时不进行取消树状数组,直接重建,因为树状数组有很多加法、判断之类的,而memset快得多
const int BUILDLIMIT = 5e4;

int ans[MAXN + 5];
bool vis[MAXN + 5];

//返回更新操作进行的次数
inline int calc(const int &tqtop) {
    int cnt = 0;
    for(int i = 1, nxt; i <= tqtop; i = nxt) {
        for(nxt = i + 1; nxt <= tqtop && tq[i].x == tq[nxt].x; ++nxt);
        for(int j = i; j < nxt; ++j) {
            if((tq[j].top & 3) == 1) {
                add(tq[j].d1, 1);
                ++cnt;
            } else if((tq[j].top & 3) == 0) {
                add(tq[j].d1, -1);
                ++cnt;
            }
        }
        for(int j = i; j < nxt; ++j) {
            if((tq[j].top & 3) == 2) {
                //前面的转换已经把l可能产生的贡献已经清除了
                ans[tq[j].top >> 2] -= sum(tq[j].d1);
            } else if((tq[j].top & 3) == 3) {
                ans[tq[j].top >> 2] += sum(tq[j].d1);
                //op2和op3都是成对出现的,只对其中一个赋值就可以了
                vis[tq[j].top >> 2] = true;
            }
        }
    }
    return cnt;
}

int n, m;

void solve(const int &l, const int &r, bool firstlayer = false) {
    if(l == r)
        return;
    if(!firstlayer) {
        bool all1 = true;
        for(int i = l; i <= r ; i++) {
            if((q[i].top & 3) >= 2) {
                all1 = false;
                break;
            }
        }
        if(all1) {
            sort(q + l, q + r + 1);
            return;
        }
    }
    int mid = (l + r) >> 1;
    solve(l, mid);
    solve(mid + 1, r);
    int tqtop = before_calc(l, r, mid);
    int cnt = calc(tqtop);
    if(!firstlayer) {
        if(cnt < BUILDLIMIT) {
            for(int j = 1; j <= tqtop; ++j) {
                if((tq[j].top & 3) == 1) {
                    add(tq[j].d1, -1);
                } else if((tq[j].top & 3) == 0) {
                    add(tq[j].d1, +1);
                }
            }
        } else
            clear();
        after_calc(l, r, mid);
    }
    return;
}

int a[MAXN + 5], b[MAXN + 5];

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    Read(n), Read(m);
    int qtop = 0;

    for(int i = 1; i <= n; ++i) {
        Read(a[i]);
        b[i] = (a[i] == a[i - 1]) ? 0 : a[i];

        q[++qtop].top = 1;
        q[qtop].x = i;
        q[qtop].d1 = b[i] + 1;
    }

    for(int i = 1, op; i <= m; ++i) {
        Read(op);
        if(op == 1) {
            int x, y;
            Read(x), Read(y);
            q[++qtop].top = i << 2;
            q[qtop].x = x;
            q[qtop].d1 = b[x] + 1;

            q[++qtop].top = i << 2;
            q[qtop].x = x + 1;
            q[qtop].d1 = b[x + 1] + 1;

            a[x] = y;
            b[x] = (a[x] == a[x - 1]) ? 0 : a[x];
            b[x + 1] = (a[x + 1] == a[x]) ? 0 : a[x + 1];

            q[++qtop].top = i << 2 | 1;
            q[qtop].x = x;
            q[qtop].d1 = b[x] + 1;

            q[++qtop].top = i << 2 | 1;
            q[qtop].x = x + 1;
            q[qtop].d1 = b[x + 1] + 1;
        } else {
            int l, r, x, y;
            Read(l), Read(r), Read(x), Read(y);
            //op2是要被减去的,op3是要被加上的
            q[++qtop].top = i << 2 | 2;
            //这里询问的就是要l而不是l-1,为了把l可能产生的贡献去掉然后加回来
            q[qtop].x = l;
            q[qtop].d1 = y + 1;

            q[++qtop].top = i << 2 | 3;
            //这里询问的就是要l而不是l-1,为了把l可能产生的贡献去掉然后加回来
            q[qtop].x = l;
            q[qtop].d1 = x - 1 + 1;

            q[++qtop].top = i << 2 | 3;
            q[qtop].x = r;
            q[qtop].d1 = y + 1;

            q[++qtop].top = i << 2 | 2;
            q[qtop].x = r;
            q[qtop].d1 = x - 1 + 1;

            //把l的贡献加回来
            if(x <= a[l] && a[l] <= y)
                ++ans[i];
        }
    }

    solve(1, qtop, true);
    for(int i = 1; i <= m; ++i) {
        if(vis[i])
            Write(ans[i]);
    }
}

595ms,28MB的极致速度与内存,去掉了一些多余的东西,这个应该是使用归并排序方法里面的最省空间的一种表达了,假如加上一个整数数组通过多一次间址来避免拷贝会不会更好?答案是否定的,整型数组还不如弄个bool数组直接标记某个状态无效(或者发挥你的想象力把这个bool数组合并到top的最高位)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

namespace FastIO {
#define BUF_SIZE 1000000
    bool IOError = 0;
    inline char NextChar() {
        static char buf[BUF_SIZE], *pl = buf + BUF_SIZE, *pr = buf + BUF_SIZE;
        if(pl == pr) {
            pl = buf, pr = buf + fread(buf, 1, BUF_SIZE, stdin);
            if(pr == pl) {
                IOError = 1;
                return -1;
            }
        }
        return *pl++;
    }
#undef BUF_SIZE

    inline bool Blank(char c) {
        return c == ' ' || c == '\n' || c == '\r' || c == '\t';
    }

    template<class T> inline void Read(T &x) {
        char c;
        while(Blank(c = NextChar()));
        if(!IOError) {
            for(x = 0; '0' <= c && c <= '9'; c = NextChar())
                x = (x << 3) + (x << 1) + c - '0';
        }
    }

    template<class T> inline void PutChar(T x) {
        if(x > 9)
            PutChar(x / 10);
        putchar(x % 10 + '0');
    }

    template<class T> inline void Write(T &x) {
        PutChar(x);
        putchar('\n');
    }

}

using namespace FastIO;

const int MAXN = 2e5;

//因为题目的数据有0,所以全部+1偏移了一位
const int H = 2e5 + 1;
int bit[H + 5];

inline void clear() {
    memset(bit, 0, sizeof(bit));
}

inline void add(int x, int v) {
    for(; x <= H; x += x & -x) {
        bit[x] += v;
    }
}

inline void set0(int x) {
    for(; x <= H; x += x & -x) {
        bit[x] = 0;
    }
}

inline int sum(int x) {
    int res = 0;
    for(; x ; x -= x & -x) {
        res += bit[x];
    }
    return res;
}

struct Query {
    int top, x, d1;
    bool operator<(const Query &q)const {
        return x < q.x;
    }
} q[5 * MAXN + 5], tq[5 * MAXN + 5];

inline int before_calc(const int &l, const int &r, const int &mid) {
    int i = l, j = mid + 1, top = 0;
    while(i <= mid || j <= r) {
        if(i > mid || (j <= r && q[j].x < q[i].x)) {
            //把右边的询问加进来
            if((q[j].top & 3) >= 2)
                tq[++top] = q[j];
            ++j;
        } else {
            //把左边的修改加进来
            if((q[i].top & 3) <= 1)
                tq[++top] = q[i];
            ++i;
        }
    }
    return top;
}

inline void after_calc(const int &l, const int &r, const int &mid) {
    merge(q + l, q + mid + 1, q + mid + 1, q + r + 1, tq + 1);
    for(int j = l; j <= r; ++j)
        q[j] = tq[j - l + 1];
}

int ans[MAXN + 5];
bool vis[MAXN + 5];

//返回更新操作进行的次数
inline int calc(const int &tqtop) {
    int cnt = 0;
    for(int i = 1, nxt; i <= tqtop; i = nxt) {
        for(nxt = i + 1; nxt <= tqtop && tq[i].x == tq[nxt].x; ++nxt);
        for(int j = i; j < nxt; ++j) {
            //把修改操作执行
            if((tq[j].top & 3) == 1) {
                add(tq[j].d1, 1);
                ++cnt;
            } else if((tq[j].top & 3) == 0) {
                add(tq[j].d1, -1);
                ++cnt;
            }
        }
        for(int j = i; j < nxt; ++j) {
            //把询问操作执行
            if((tq[j].top & 3) == 2) {
                //前面的转换已经把l可能产生的贡献已经清除了
                ans[tq[j].top >> 2] -= sum(tq[j].d1);
            } else if((tq[j].top & 3) == 3) {
                ans[tq[j].top >> 2] += sum(tq[j].d1);
                //op2和op3都是成对出现的,只对其中一个赋值就可以了
                vis[tq[j].top >> 2] = true;
            }
        }
    }
    return cnt;
}

//超过该界时不进行取消树状数组,直接重建,因为树状数组有很多加法、判断之类的,而memset快得多
const int BUILDLIMIT = 5e4;

void solve(const int &l, const int &r, bool fl = false) {
    if(l == r)
        return;
    if(!fl) {
        //only update,这个剪枝应该功能强大
        bool ou = true;
        for(int i = l; i <= r ; i++) {
            //发现询问操作,break
            if((q[i].top & 3) >= 2) {
                ou = false;
                break;
            }
        }
        if(ou) {
            sort(q + l, q + r + 1);
            return;
        }
    }
    int mid = (l + r) >> 1;
    solve(l, mid);
    solve(mid + 1, r);
    int tqtop = before_calc(l, r, mid);
    int cnt = calc(tqtop);
    if(!fl) {
        if(cnt < BUILDLIMIT) {
            for(int j = 1; j <= tqtop; ++j) {
                if((tq[j].top & 3) == 1) {
                    add(tq[j].d1, -1);
                } else if((tq[j].top & 3) == 0) {
                    add(tq[j].d1, +1);
                }
            }
        } else
            clear();
        after_calc(l, r, mid);
    }
    return;
}

int a[MAXN + 5], b[MAXN + 5];

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku

    int n, m;
    Read(n), Read(m);
    int qtop = 0;

    for(int i = 1; i <= n; ++i) {
        Read(a[i]);
        b[i] = (a[i] == a[i - 1]) ? 0 : a[i];

        q[++qtop].top = 1;
        q[qtop].x = i;
        q[qtop].d1 = b[i] + 1;
    }

    for(int i = 1, op; i <= m; ++i) {
        Read(op);
        if(op == 1) {
            int x, y;
            Read(x), Read(y);
            q[++qtop].top = i << 2;
            q[qtop].x = x;
            q[qtop].d1 = b[x] + 1;

            q[++qtop].top = i << 2;
            q[qtop].x = x + 1;
            q[qtop].d1 = b[x + 1] + 1;

            a[x] = y;
            b[x] = (a[x] == a[x - 1]) ? 0 : a[x];
            b[x + 1] = (a[x + 1] == a[x]) ? 0 : a[x + 1];

            q[++qtop].top = i << 2 | 1;
            q[qtop].x = x;
            q[qtop].d1 = b[x] + 1;

            q[++qtop].top = i << 2 | 1;
            q[qtop].x = x + 1;
            q[qtop].d1 = b[x + 1] + 1;
        } else {
            int l, r, x, y;
            Read(l), Read(r), Read(x), Read(y);
            //op2是要被减去的,op3是要被加上的
            q[++qtop].top = i << 2 | 2;
            //这里询问的就是要l而不是l-1,为了把l可能产生的贡献去掉然后加回来
            q[qtop].x = l;
            q[qtop].d1 = y + 1;

            q[++qtop].top = i << 2 | 3;
            //这里询问的就是要l而不是l-1,为了把l可能产生的贡献去掉然后加回来
            q[qtop].x = l;
            q[qtop].d1 = x - 1 + 1;

            q[++qtop].top = i << 2 | 3;
            q[qtop].x = r;
            q[qtop].d1 = y + 1;

            q[++qtop].top = i << 2 | 2;
            q[qtop].x = r;
            q[qtop].d1 = x - 1 + 1;

            //把l的贡献加回来
            if(x <= a[l] && a[l] <= y)
                ++ans[i];
        }
    }

    solve(1, qtop, true);
    for(int i = 1; i <= m; ++i) {
        if(vis[i])
            Write(ans[i]);
    }
}
posted @ 2019-09-11 16:01  Inko  阅读(456)  评论(2编辑  收藏  举报