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();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话