省选测试4

省选测试 4

T1

​ 给你一个 \(n\) 维的多面体,定义它的面是一个 \(n-1\) 维多面体,已知它必定有 \(2n\)面。

​ 我们把这个 \(n\) 维体的所有面都染色。

​ 现在,给你这个多面体每一维的长度,然后可以将这个\(n\)维体划分成 \(\displaystyle \sum_{i=1}^na_i\)个每个边的边长为1的小 \(n\) 维体。

​ 定义一个小 \(n\) 维体的权值是它有多少个面被染色。

​ 输出每个权值的小 \(n\) 维体的个数,显然可以知道,权值范围在 [0, \(2n\)] 中,所以只需要把 [0, \(2n\)] 的所有答案都输出即可。

​ 答案对469762049取模. \(n <= 100000\)

​ 多项式.

\(a_i = 1\)时, 我们发现原来所有的小\(i- 1\)椎体都会增加染色面两个面变成\(i\)锥体.

\(a_i >= 2\)时, 我们发现只有两遍的小\(i - 1\)椎体会增加一个染色面, 中间的那些虽然也增加了两个面但是并没有被染色, 所以染色面数不变.

​ 所以上面两种情况分别对应着多项式 : \(x^2, 2x+(a_i - 2)\).

​ 然后用分治NTT就好了. 但是我还不会, 于是我用了分治 + NTT, 时间复杂度还是\(O(n\log n)\)的.

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

const int N = 3e5 + 5, mod = 469762049, g = 3;
int n;
int a[N];
int F[N << 1], rev[N << 1];

int ksm(int x, int y) {
    int res = 1;
    while(y) { if(y & 1) res = 1ll * res * x % mod; x = 1ll * x * x % mod; y >>= 1; }
    return res;
}

void NTT(int len, int *a, int f) {
    for(int i = 0;i < len; i++) if(rev[i] > i) swap(a[i], a[rev[i]]);
    for(int i = 1;i < len; i <<= 1) {
        int w = ksm(g, (mod - 1) / (i << 1));
        if(f == -1) w = ksm(w, mod - 2);
        for(int j = 0;j < len; j += (i << 1)) {
            int k = 1;
            for(int l = 0;l < i; l++, k = 1ll * k * w % mod) {
                int x = a[j + l], y = 1ll * k * a[j + l + i] % mod;
                a[j + l] = (x + y) % mod;
                a[j + l + i] = (x - y + mod) % mod;
            }
        }
    }
    if(f == -1) {
        int INV = ksm(len, mod - 2);
        for(int i = 0;i < len; i++) a[i] = 1ll * a[i] * INV % mod;
    }
}

void Divide(int l, int r, int *f, int L) {
    if(l > r) return ;
    if(l == r) {
        if(a[l] == 1) f[2] = 1;
        if(a[l] >= 2) f[0] = (a[l] - 2), f[1] = 2;
        return ;
    }
    int mid = (l + r) >> 1;
    Divide(l, mid, f, (mid - l + 1) * 2 + 1); 
    int g[L << 2];
    memset(g, 0, sizeof(g));
    Divide(mid + 1, r, g, (r - mid) * 2 + 1);
    // cout << l << " " << r << "------------->\n";
    // for(int i = 0;i <= (mid - l + 1) * 2 + 1; i++) cout << f[i] << " "; cout << "\n";
    // for(int i = 0;i <= (r - mid) * 2 + 1; i++) cout << g[i] << " "; cout << "\n";
    int len = 1, t = -1;
    while(len < (L << 1)) len <<= 1, t ++;
    for(int i = 0;i < len; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << t);
    NTT(len, f, 1); NTT(len, g, 1);
    for(int i = 0;i < len; i++) f[i] = 1ll * f[i] * g[i] % mod;
    NTT(len, f, -1);
    for(int i = L;i <= len; i++) f[i] = 0;
}

int main() {

    freopen("poly.in","r",stdin); freopen("poly.out","w",stdout);

    n = read();
    for(int i = 1;i <= n; i++) a[i] = read();
    Divide(1, n, F, 2 * n + 1);
    for(int i = 0;i <= 2 * n; i++) printf("%d\n", F[i]);

    fclose(stdin); fclose(stdout);

    return 0;
}

/*
3
200 203 15
*/

T2

​ 给定\(n\)个形如\(y = kx +b\)的方程,有\(m\)个操作.

\(opt=1 : L, R, x,\)\(x\)带入区间\([L,R]\)的方程求最大值.

\(opt=2 : p, v\)\(k_p\)增加为\(v\).

\(opt=3 : L, R, v\)\(b_L...b_R += v\).

\(n <= 200000, m <= 2000000\).

​ 分块 + 李超线段树.

​ 之前没有学过李超线段树, 学习了一下, 发现这类维护方程的题都可以用它做, 还是很妙的.

​ 先分块, 然后对每个块内的所有方程放到一个李超线段树中去维护. 注意有一个细节 : 李超线段树中直接存\(k, b\)比存最优线段的标号更加方便, 因为更改的时候存标号的话可能会有冲突.

​ 对于单点修改\(k\), 我们相当于得到了一条新的直线, 直接插到李超线段树中就好了, 为什么不用删除呢? 因为原来的那条直线一定不优了.

​ 对于区间修改\(b\), 我们分块改, 对于散块暴力改, 对于整块, 那就相当于把这一块内的所有直线都向上平移了一段, 所以只需要维护一个整块加的标记就好了.

\(O(n \sqrt n \log n)\)

#include <bits/stdc++.h>

#define ls(o) t[o].ls
#define rs(o) t[o].rs
#define mid ((l + r) >> 1)

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f, 1);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

const int N = 2e5 + 5, M = 550, inf = 1000;
int n, m, tot, c_;
int L[N], R[N], rt[N], pos[N];
long long add_b[N];
struct Line { long long k, b; } a[N];
struct point { int ls, rs, Max; long long k, b; } t[N * 50];

inline long long F(long long k, long long b, int i, int x) {
    return 1ll * x * k + b + add_b[pos[i]];
}

inline double get_inter(long long k, long long b, long long K, long long B) {
    return 1.0 * (B - b) / (k - K);
}

struct Lichao_tree {

    void change(int &o, int l, int r, long long K, long long B, int i) {
        if(!o) {
            o = ++ tot; t[o].k = K; t[o].b = B; t[o].Max = i;
            return ;    
        }
        if(l == r) {
            long long x = F(t[o].k, t[o].b, t[o].Max, l), y = F(K, B, i, l);
            if(y > x) t[o].k = K, t[o].b = B, t[o].Max = i;
            return ;
        }
        long long x = F(t[o].k, t[o].b, t[o].Max, l), y = F(t[o].k, t[o].b, t[o].Max, r), x_ = F(K, B, i, l), y_ = F(K, B, i, r);
        double z = get_inter(t[o].k, t[o].b, K, B);
        if(x_ > x && y_ > y) { t[o].k = K; t[o].b = B; t[o].Max = i; return ; }
        if(x_ <= x && y_ <= y) { return ; }
        if(x_ > x && y_ <= y) {
            if(z <= mid) change(ls(o), l, mid, K, B, i);
            if(z > mid) { change(rs(o), mid + 1, r, t[o].k, t[o].b, t[o].Max); t[o].k = K; t[o].b = B; t[o].Max = i; }
        }
        if(x_ <= x && y_ > y) {
            if(z <= mid) { change(ls(o), l, mid, t[o].k, t[o].b, t[o].Max); t[o].k = K; t[o].b = B; t[o].Max = i; }
            if(z > mid) change(rs(o), mid + 1, r, K, B, i);
        } 
    }
    
    long long query(int o, int l, int r, int k) {
        if(!o) return 0;
        if(l == r) return F(t[o].k, t[o].b, t[o].Max, k);
        long long res = F(t[o].k, t[o].b, t[o].Max, k);
        if(k <= mid) return max(res, query(ls(o), l, mid, k));
        if(k > mid) return max(res, query(rs(o), mid + 1, r, k));
    }

} T[M];

void build() {
    for(register int i = 1;i <= n; i++) T[pos[i]].change(rt[pos[i]], 1, inf, a[i].k, a[i].b, i);
}

inline long long query(int l, int r, int k) {
    int x = pos[l], y = pos[r];
    // cout << x << " " << y << "---------->\n";
    long long res = 0;
    if(x == y) {
        for(register int i = l;i <= r; i++) res = max(res, F(a[i].k, a[i].b, i, k)); 
    }
    else {
        // cout << T[x].query(rt[x], 1, inf, k) << "\n";
        for(register int i = l;i <= R[x]; i++) res = max(res, F(a[i].k, a[i].b, i, k));
        
        for(register int i = x + 1;i <= y - 1; i++) res = max(res, T[i].query(rt[i], 1, inf, k));

        for(register int i = L[y];i <= r; i++) res = max(res, F(a[i].k, a[i].b, i, k));
    }
    return res;
}

inline void change_k(int p, int v) {
    a[p].k += v;
    int x = pos[p];
    T[x].change(rt[x], 1, inf, a[p].k, a[p].b, p);
}

inline void change_b(int l, int r, int v) {
    int x = pos[l], y = pos[r];
    if(x == y) {
        for(register int i = l;i <= r; i++) {
            a[i].b += v; T[x].change(rt[x], 1, inf, a[i].k, a[i].b, i);
        }
    }
    else {
        for(register int i = l;i <= R[x]; i++) {
            a[i].b += v; T[x].change(rt[x], 1, inf, a[i].k, a[i].b, i);
        }

        for(register int i = x + 1;i <= y - 1; i++) add_b[i] += v;

        for(register int i = L[y];i <= r; i++) {
            a[i].b += v; T[y].change(rt[y], 1, inf, a[i].k, a[i].b, i);
        }
    }
}

int main() {

    // freopen("data_struct.in","r",stdin); freopen("data_struct.out","w",stdout);

    n = read(); m = read(); int B = sqrt(n); c_ = n;
    for(int i = 1;i <= n; i++) a[i].k = read();
    for(int i = 1;i <= n; i++) a[i].b = read();
    for(int i = 1;i <= n; i++) L[i] = 2333333;
    for(int i = 1;i <= n; i++) pos[i] = (i - 1) / B + 1;
    for(int i = 1;i <= n; i++) L[pos[i]] = min(L[pos[i]], i), R[pos[i]] = max(R[pos[i]], i);
    // for(int i = 1;i <= n; i++) cout << F(i, 1) << "\n";
    build();
    for(int i = 1, opt, l, r, x;i <= m; i++) {
        opt = read();
        if(opt == 1) {
            l = read(); r = read(); x = read();
            printf("%lld\n", query(l, r, x));
        }
        if(opt == 2) {
            l = read(); x = read();
            change_k(l, x);
        }
        if(opt == 3) {
            l = read(); r = read(); x = read();
            change_b(l, r, x);
        }
    }


    // fclose(stdin); fclose(stdout);

    return 0;
}

T3

​ 到现在题目还没读懂......

​ 从上次打完最后的副本之后, 她决心想要出一个有价值的题,然后就有了这道题

​ 现在, 她想给小朋友们发糖(大雾),她现在有 n 个薛定谔的糖果盒,她可以进行 m 次如下的三个操作

  1. +1s
  2. 选出 k 个糖果盒,每个糖果盒有 p% 的概率开出糖果。(可以包含已经打开的糖果盒,由于每个糖果盒是量子态的,所以可能之前有糖果的糖果盒这次扣上盒盖的后,可能会变成一个空盒)
  3. 选出来 T 个礼品盒,第 i 个糖果盒有min(P + i - 1,100)% 的概率开出糖果
    现在ta想要知道,她期望最多能拿出多少个糖果。
    结果保留 5 位小数即可

\(P <= 100, T <= 20, N <= 500, M <= 500,Test <= 10\)

posted @ 2021-03-04 06:22  C锥  阅读(117)  评论(0编辑  收藏  举报