20251014 杂题

图片

atcoder一类常见的折半问题.

枚举子集和枚举超集可以均摊,改为枚举一半子集,一半超级.

具体地:

图片

其中枚举子集我写的太劣了还T了

图片


P7252 [JSOI2011] 棒棒糖

区间查询绝对众数.

首先直接随机化就可以过,每次随机一个数钦定他是绝对众数,正确率很高.

然后一种确定性做法是回滚莫队(不是)

log做法是:

弄一个可持久化线段树,权值开,,每个版本对应下标前缀和

然后扫描线直接查即可

图片

点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

// 权值主席树(persistent segment tree)实现
// 值域 [1..MAXV]
const int MAXN = 50000 + 5;
const int MAXV = 50000; // 按题意 c_i <= 5e4
// 估计节点上限: n * (log2(MAXV)+2) 安全取 20 * MAXN
const int MAXNODE = MAXN * 20;

int lc[MAXNODE], rc[MAXNODE], sumv[MAXNODE];
int roots[MAXN]; // roots[i] 表示前缀 i 的版本(i 从 0 开始,roots[0] = 0)
int tot = 1; // 0 号节点为空节点,节点编号从 1 开始

int newnode() {
    // 返回一个新节点编号(已清零)
    ++tot;
    lc[tot] = rc[tot] = 0;
    sumv[tot] = 0;
    return tot;
}

// 在 prev 版本的树上插入位置 pos,返回新版本根节点编号
int update(int prev, int l, int r, int pos) {
    int cur = newnode();
    lc[cur] = lc[prev];
    rc[cur] = rc[prev];
    sumv[cur] = sumv[prev] + 1;

    if (l == r) {
        return cur;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid) {
        lc[cur] = update(lc[prev], l, mid, pos);
    } else {
        rc[cur] = update(rc[prev], mid + 1, r, pos);
    }
    return cur;
}

// 在区间 [l,r] 中,比较两个版本 u (root_r) 和 v (root_{l-1}),长度 len,寻找多数元素的值或 0
int query_major(int u, int v, int l, int r, int len) {
    if (l == r) {
        int cnt = sumv[u] - sumv[v];
        if (cnt > len / 2) return l;
        else return 0;
    }
    int mid = (l + r) >> 1;
    int cntLeft = sumv[ lc[u] ] - sumv[ lc[v] ];
    if (cntLeft > len / 2) {
        return query_major(lc[u], lc[v], l, mid, len);
    }
    int total = sumv[u] - sumv[v];
    int cntRight = total - cntLeft;
    if (cntRight > len / 2) {
        return query_major(rc[u], rc[v], mid + 1, r, len);
    }
    return 0;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, m;
    if (!(cin >> n >> m)) return 0;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i) cin >> a[i];

    // 初始化空节点 0 的数据(tot 从 1 开始,所以 0 作为空节点)
    lc[0] = rc[0] = 0;
    sumv[0] = 0;
    // 确保 tot >= 1 且节点 1 留作第一个 newnode() 的结果
    tot = 1; 
    lc[1] = rc[1] = 0;
    sumv[1] = 0;

    roots[0] = 0;
    // 构建每个前缀的版本
    for (int i = 1; i <= n; ++i) {
        // 在 roots[i-1] 上插入 a[i]
        roots[i] = update(roots[i-1], 1, MAXV, a[i]);
    }

    while (m--) {
        int l, r;
        cin >> l >> r;
        int len = r - l + 1;
        int ans = query_major(roots[r], roots[l - 1], 1, MAXV, len);
        cout << ans << '\n';
    }
    return 0;
}

---

P3594 [POI 2015 R3] 狼坑 Trous de loup

无敌好题.

枚举区间,再枚举减去的东西 n^3

枚举改成双指针 n^2

发现中间删除那一段是
图片

可以用单调队列维护.

双指针过程中,右指针移动时维护单调,左指针移动时维护合法

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


#define maxn 2000005
long long n, p, d, ans;
long long a[maxn], sum[maxn];
int l, h, t, q[maxn];

signed main() {
	if (!(cin >> n >> p >> d)) return 0;
	
	for(int i = 1; i <= n; ++ i) {
		if (!(cin >> a[i])) return 0;
		sum[i] = sum[i - 1] + a[i];
	}
	
	ans = d, q[t] = d, l = 1;
	
	for(int i = d + 1; i <= n; ++ i) {
		while(h <= t && sum[i] - sum[i - d] > sum[q[t]] - sum[q[t] - d]) -- t;
		q[++ t] = i;
		while(h <= t && sum[i] - sum[l - 1] - sum[q[h]] + sum[q[h] - d] > p) {
			++ l;
			while(h <= t && q[h] - d + 1 < l) ++ h;
		}
		ans = max(ans, (long long)i - l + 1);
	}
	
	cout << ans << endl;
	
	return 0;
}

是单调的

posted @ 2025-10-14 21:52  Dreamers_Seve  阅读(6)  评论(0)    收藏  举报