E. Army Creation

E. Army Creation

https://codeforces.ml/problemset/problem/813/E

题意:

多次询问给你一段区间 每个数至多可以选取k个问最多可以选的个数

思路

对于一个询问区间 相同的数我们就选取后k个数 那么我们只要记录询问区间内每个数后第k个相同的数的位置(b[i],无后第k个数b[i]赋值为n+1即可) 如果这个第k个相同的数也在询问区间内说明当前这个数我们不选 那么区间内b[i]>r的数的个数就是答案
可以用主席树做也可以用普通线段树做(维护 b[i])

F1.主席树

用rt数组代表新的树的根节点编号
维护一个区间内b[i]的个数 即tree[].sum
每次询问一个区间[l, r]那么我们就要寻找b[i]在区间范围[r+1, n+1]内的个数 那么询问区间就是[r+1,n+1]
我们还要确保 选取的b[i]中的i是在[l, r]范围内的
而因为每次更新的顺序是从左到右 每次插入一个b[i]就会有一棵新的树
所以让rt[r]这颗线段树在区间[r+1, n+1]的答案减去rt[l - 1]这棵树这颗线段树在区间[r+1, n+1]的答案 就是[l, r]区间内b[i] > r的个数 即所求答案

#include<bits/stdc++.h>
#include<unordered_map>
#define ll long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-4;
const ll N = 1e5 + 5;
const int M = 1e5 + 5;
const int mod = 1e9 + 7;
ll n, k, a[N], b[N], q, last;
vector<ll>v[N];
struct node {
	ll ls, rs, sum;
}tree[N * 20];//不能开太小 一般开32倍N 但也有可能太大超空间 适当减小
ll rt[N], sz;
//更新 x代表新节点的编号 因为普通值传递不会改变原地址中的数的值 所以用引用
void update(ll l, ll r, ll &x, ll y, ll num){
	x = ++sz;
        //继承上一课树的信息
	tree[x] = tree[y];
	tree[x].sum++;
	if (l == r) return;
	ll mid = (l + r) / 2;
        //完成整棵树的更新 
	if (num <= mid) update(l, mid, tree[x].ls, tree[y].ls, num);
	else update(mid + 1, r, tree[x].rs, tree[y].rs, num);
}


//询问 
ll query(ll u, ll v, ll l, ll r, ll ql, ll qr) {
	if (ql <= l && qr >= r) {
		return tree[v].sum - tree[u].sum;
	}
	ll mid = (l + r) / 2;
	if (mid >= ql) 
		return query(tree[u].ls, tree[v].ls, l, mid, ql, qr) + tree[tree[v].rs].sum - tree[tree[u].rs].sum;
	return query(tree[u].rs, tree[v].rs, mid + 1, r, ql, qr);
}


void solve()
{
	cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
                //按顺序储存值为a[i]的数的位置
		v[a[i]].push_back(i);
	}
        //记录b[i]
	for (int i = 1; i <= 1e5; i++) {
		for (int j = 0; j < v[i].size(); j++) {
                        后没有k个与之相等的数
			if (j + k >= v[i].size()) b[v[i][j]] = n + 1;
			else b[v[i][j]] = v[i][j + k];
		}
	}
	for (int i = 1; i <= n; i++) {
		update(1, n+1, rt[i], rt[i - 1], b[i]);
	}
	cin >> q;
	ll x, y;
	for (int i = 1; i <= q; i++) {
		cin >> x >> y;
		x = (last + x) % n + 1;
		y = (last + y) % n + 1;
		if (x > y) swap(x, y);
                //[y+1, n+1]为询问区间 
		last = query(rt[x - 1], rt[y], 1, n+1, y + 1, n + 1);
		cout << last << "\n";

	}
}

signed main()
{

	IOS;
	int t = 1;
	//cin >> t;
	while (t--)
	{
		solve();
	}

}

F2.线段树

线段树可以维护一个区间中包含了这个区间所有b[i]的容器
每次查询一个区间 二分查找这个区间第一个大于r的数的位置 答案就是 区间长度-这个位置
需保证容器中b[i]有序
线段树的push_up部分可以用stl中的merge函数
v1代表要合并的第一个容器 v2代表要合并的第二个容器 v3代表储存合并后的新容器(v3的大小应该先resize成前两个容器的大小和,这样才能保证装下)
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());

#include<bits/stdc++.h>
#include<unordered_map>
#define ll long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-4;
const ll N = 1e6 + 5;
const int M = 1e5 + 5;
const int mod = 1e9 + 7;
ll n, k, a[N], b[N], q, last;
vector<ll>v[N];
struct node {
	ll l, r;
        //用于储存b[i]的容器
	vector<ll>bb;
}tree[N << 2];


void push_up(ll p) {
	merge(tree[p << 1].bb.begin(), tree[p << 1].bb.end(), tree[p << 1|1].bb.begin(), tree[p << 1|1].bb.end(), tree[p].bb.begin());
}

void build(ll l, ll r, ll p) {
	tree[p].l = l, tree[p].r = r;
	if (l == r) {
		tree[p].bb.push_back(b[l]);
		return;
	}
	ll mid = (l + r) / 2;
	build(l, mid, p << 1);
	build(mid + 1, r, p << 1 | 1);
        //先让父结点的容器足够大 才能装下合并后的子结点容器
	tree[p].bb.resize(tree[p << 1].bb.size() + tree[p << 1|1].bb.size());
	push_up(p);
}

ll query(ll ql, ll qr, ll p) {
	ll l = tree[p].l, r = tree[p].r, ans = 0;
	if (l >= ql && r <= qr) {
                //二分查找第一个大于qr的第一个位置
		ll xx = upper_bound(tree[p].bb.begin(), tree[p].bb.end(), qr) - tree[p].bb.begin();
		ans += tree[p].bb.size() - xx;
		return ans;
	}
	ll mid = (l + r) / 2;
        //往两边继续询问 加上答案值
	if (ql <= mid) ans += query(ql, qr, p << 1);
	if (qr > mid)  ans += query(ql, qr, p << 1|1);
	return ans;
}


void solve()
{
	cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		v[a[i]].push_back(i);
	}
	for (int i = 1; i <= 1e5; i++) {
		for (int j = 0; j < v[i].size(); j++) {
			if (j + k >= v[i].size()) b[v[i][j]] = n + 1;
			else b[v[i][j]] = v[i][j + k];
		}
	}
	build(1, n, 1);
	cin >> q;
	ll x, y;
	for (int i = 1; i <= q; i++) {
		cin >> x >> y;
		x = (last + x) % n + 1;
		y = (last + y) % n + 1;
		if (x > y) swap(x, y);
		last = query(x, y, 1);
		cout << last << "\n";

	}
}

signed main()
{

	IOS;
	int t = 1;
	//cin >> t;
	while (t--)
	{
		solve();
	}

}
posted @   Yaqu  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示