3.4考试总结

3.4模拟赛

这场考试让我意识到我的很多小毛病根本没有改正

经验教训:

  1. #define int long long
  2. 审题不仔细,时间短了以后心态不正
  3. 盲目做题,没有在已有的性质上进一步考虑

T1

\(sto~郭队~orz\),场切T1

题目描述:小火车沉迷垃圾手游不能自拔,他还在玩碧蓝航线。

为了庆祝小火车打捞出了加贺赤城,他决定让你搭建一座纪念塔群,纪念塔共有 \(n\) 个排成一排,第 \(i\) 个高度为 \(H_i\),也就是由 \(H_i\) 块砖头组成,你得一块一块砖头搭建。每次你至多能携带 \(k\) 块砖头,由任意一座塔的底端开始,可以向上移动或者向左右两座塔的同高度移动(前提是那些位置上有砖块),也可以在那些位置摆上砖块(即使是悬空的),并且一旦摆上砖块你就得立刻移动过去。请问你最少需要多少次才能搭建完呢?

\(1 \le n \le 10^5\) \(1\le a_i\le 10^9\)

我们考虑根据各个点建一棵笛卡尔树,那么我们后面可以直接在笛卡尔树上递归处理这棵树即可

inline void dfs(int x, int fa) {
    siz[x] = 1;//siz存子树大小,sum存值
    if(ls) dfs(ls, x), sum[x] += sum[ls], siz[x] += siz[ls];
    if(rs) dfs(rs, x), sum[x] += sum[rs], siz[x] += siz[rs];
    sum[x] += siz[x] * (h[x] - h[fa]);
    if(sum[x] > 0){
        int tot = ceil(1.0 * sum[x] / k);
        ans += tot, sum[x] -= tot * k;
    }
}//统计答案

signed main() {
    n = read(), k = read();
    for(int i = 1; i <= n; ++i) h[i] = read();
    for(int i = 1; i <= n; ++i, res = 0) {
        while(top && h[stk[top]] > h[i]) res = stk[top--];
        t[i].l = res, t[stk[top]].r = i, stk[++top] = i;
    }//建树操作
    rt = stk[1];
    dfs(rt, 0);
    write(ans), puts("");
    return 0;
}

T2

小火车沉迷垃圾手游不能自拔,正在玩碧蓝航线,可惜小火车的舰(lao)队(po)练度太低打不过副本,所以他只好去刷其余的副本来升级。

总共有 \(n\) 个副本编号从 \(1\)\(n\),每个副本有个难度值 \(a_i\),小火车每天按照顺序刷连续的一段副本,第 \(j\) 天会刷的副本必须落在 \(l\)\(r\) 之间。但是他是个很懒的人,每天一开始他的懒惰值为 \(0\),当他刷完一个副本以后懒惰值就会异或上 \(a_i\),小火车希望懒惰值一直保持单调不下降,请问每天他都有多少种刷副本的方法?

题意说的很明确,那么我们考虑,如果我们固定从一个点开始往后进行异或,那么一定是会有一个最靠右的点,那么就要考虑怎么找到那个点。

\(zzz\) 讲了个极端好的思路,即我们首先对于异或进行前缀和操作,我们发现其实对于原式来说异或上一个 \(a\),想要变小 当且仅当 最高位的值为1,却同时另外一个数的最高值也为1。因此,我们只关心 \(a\) 的最高位的一的位置,找一下它会使得哪个 \(b\) 变小

那么我们记录一个东西就是我们对于 \(a_i\) 来说,在第 \(j\) 位曾经从 \(0\)->\(1\) 或者从 \(1\)->\(0\),我们记录 \(f_{0/1,i,j}\),0表示0变成了1,1表示1变成了0,这个操作使第 \(i\) 个数在第 \(j\) 位贡献的(因为我们只考虑 \(a_i\) 的最高位即可)

inline int get(int x) {
    per (i, 30, 0)
        if (x >> i & 1) return i;
    return -1;
}
rep (i, 1, n) {
    memcpy(f[0][i], f[0][i - 1], sizeof(f[0][i - 1]));
    memcpy(f[1][i], f[1][i - 1], sizeof(f[1][i - 1]));
    int h = get(a[i]);
    if (~ h) ++ f[sum[i - 1] >> h & 1][i][h];
}

然后对于每一位,我们直接二分check我们到这一位,当前位置被变换了几次,如果 1->0 大于 0 次,就说明这个位置的时候值变小了,考虑 r = mid - 1,不然的话就令 l=mid

这里我们这么写

inline bool check(int l, int r) {
    per (i, 30, 0) {
        if ((sum[l - 1] >> i & 1) && (f[0][r][i] - f[0][l - 1][i] > 0)) return false;
        if ((!(sum[l - 1] >> i & 1)) && (f[1][r][i] - f[1][l - 1][i] > 0)) return false;
    }
    return true;
}
inline int get_nxt(int x) {
    l = x, r = n, res = l;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(x, mid)) res = mid, l = mid + 1;
        else r = mid - 1;
    }
    return res;
}

最后我们统计出所有答案以后,对于每一个点建一棵支持区间加的主席树,每次令 \(i\)\(nxt_i\) 这个区间进行+1 即可,然后用 \(q\log n\) 的复杂度即可直接输出答案

inline int Len(int l, int r, int L, int R) {
    return (min(R, r) - max(L, l) + 1);
}

inline void update(int &p, int pre, int l, int r, int L, int R) {
    p = ++ cnt;
    tr[p] = tr[pre];
    if (L <= l && r <= R) {
        ++tr[p].tag;
        return ;
    }
    tr[p].sum += Len(l, r, L, R);
    int mid = (l + r) >> 1;
    if (L <= mid) update(tr[p].ls, tr[pre].ls, l, mid, L, R);
    if (R > mid) update(tr[p].rs, tr[pre].rs, mid + 1, r, L, R);
}

inline int query(int u, int v, int l, int r, int L, int R) {
    if (L <= l && r <= R)
        return tr[v].sum - tr[u].sum + (tr[v].tag - tr[u].tag) * Len(l, r, L, R);
    int mid = (l + r) >> 1;
    int res = (tr[v].tag - tr[u].tag) * Len(l, r, L, R);
    if (L <= mid) res += query(tr[u].ls, tr[v].ls, l, mid, L, R);
    if (R > mid) res += query(tr[u].rs, tr[v].rs, mid + 1, r, L, R);
    return res;
}

总复杂度为 \(n\log a_i \log n+q\log n\)

完整代码再放一下

/*
BlackPink is the Revolution
light up the sky
Blackpink in your area
*/
int n, m, T, ans, f[2][N][32], cnt, l, r, res;
int a[N], sum[N], nxt[N], rt[N];

inline int get(int x) {
    per (i, 30, 0)
        if (x >> i & 1) return i;
    return -1;
}

inline bool check(int l, int r) {
    per (i, 30, 0) {
        if ((sum[l - 1] >> i & 1) && (f[0][r][i] - f[0][l - 1][i] > 0)) return false;
        if ((!(sum[l - 1] >> i & 1)) && (f[1][r][i] - f[1][l - 1][i] > 0)) return false;
    }
    return true;
}

struct node {
    int ls, rs, sum, tag;
}tr[N << 5];


inline int get_nxt(int x) {
    l = x, r = n, res = l;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(x, mid)) res = mid, l = mid + 1;
        else r = mid - 1;
    }
    return res;
}
inline int Len(int l, int r, int L, int R) {
    return (min(R, r) - max(L, l) + 1);
}

inline void update(int &p, int pre, int l, int r, int L, int R) {
    p = ++ cnt;
    tr[p] = tr[pre];
    if (L <= l && r <= R) {
        ++tr[p].tag;
        return ;
    }
    tr[p].sum += Len(l, r, L, R);
    int mid = (l + r) >> 1;
    if (L <= mid) update(tr[p].ls, tr[pre].ls, l, mid, L, R);
    if (R > mid) update(tr[p].rs, tr[pre].rs, mid + 1, r, L, R);
}

inline int query(int u, int v, int l, int r, int L, int R) {
    if (L <= l && r <= R)
        return tr[v].sum - tr[u].sum + (tr[v].tag - tr[u].tag) * Len(l, r, L, R);
    int mid = (l + r) >> 1;
    int res = (tr[v].tag - tr[u].tag) * Len(l, r, L, R);
    if (L <= mid) res += query(tr[u].ls, tr[v].ls, l, mid, L, R);
    if (R > mid) res += query(tr[u].rs, tr[v].rs, mid + 1, r, L, R);
    return res;
}

int main(){
    read(n);
    rep (i, 1, n) read(a[i]), sum[i] = sum[i - 1] ^ a[i];
    rep (i, 1, n) {
        memcpy(f[0][i], f[0][i - 1], sizeof(f[0][i - 1]));
        memcpy(f[1][i], f[1][i - 1], sizeof(f[1][i - 1]));
        int h = get(a[i]);
        if (~ h) ++ f[sum[i - 1] >> h & 1][i][h];
    }

    rep (i, 1, n) 
        nxt[i] = get_nxt(i);
    rep (i, 1, n)
        update(rt[i], rt[i - 1], 1, n, i, nxt[i]);
    read(T);
    while (T--) {
        read(l, r);
        l = (l + ans) % n + 1, r = (r + ans) % n + 1;
        if (l > r) swap(l, r);
        write(ans = query(rt[l - 1], rt[r], 1, n, l, r), '\n');
    }
    return 0;
}
//write:RevolutionBP
posted @ 2022-03-06 12:02  RevolutionBP  阅读(31)  评论(0编辑  收藏  举报