题解:P17738 [ICPC 2019 Xuzhou R] Yuuki and a problem
算法 1
我会暴力!
考虑若当前硬币集合可以表示
时间复杂度
:::success[参考代码]
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int n, q, a[N], b[N], op, l, r;
ll ans;
int main(){
cin >> n >> q;
for(int i = 1; i <= n; i++) cin >> a[i];
while(q--){
cin >> op >> l >> r;
if(op == 1){
a[l] = r;
}
else{
for(int i = 1; i <= n; i++) b[i] = a[i];
sort(b + l, b + r + 1);
ans = 0;
for(int i = l; i <= r; i++){
if(ans+1 >= b[i]) ans += b[i];
else break;
}
cout << ans+1 << '\n';
}
}
return 0;
}
:::
算法 2
我会分块!请注意,这个方法不能通过这题,不建议学习。
看到
先仅考虑数字
考虑
然后取出其中
最终时间复杂度为
:::success[参考代码]
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10, M = 460, B = 450, inf = 1e9+1;
int n, q, a[N], op, x, y;
ll ans = 0;
struct piii{
int l, r, mn;
bool operator < (const piii & o) const{
return a[mn] > a[o.mn];
}
};
priority_queue<piii> pq;
struct Tree1{
int t[N];
void upd(int x, int v){
while(x <= n){
t[x] += v;
x += x & -x;
}
}
int qry(int x){
int res = 0;
while(x){
res += t[x];
x -= x & -x;
}
return res;
}
} c[M];
struct Tree2{
int mx[N*4], mn[N*4], L, R; ll s[N*4];
inline void mg(int & u){
mx[u] = max(mx[u*2], mx[u*2+1]);
s[u] = s[u*2] + s[u*2+1];
if(a[mn[u*2]] <= a[mn[u*2+1]]){
mn[u] = mn[u*2];
}
else{
mn[u] = mn[u*2+1];
}
}
inline void setnd(int & u, int & i){
mx[u] = s[u] = a[i];
if(a[i] <= B) mn[u] = n+1;
else mn[u] = i;
}
void build(int u, int l, int r){
if(l == r){
setnd(u, l);
return;
}
int mid = (l + r) / 2;
build(u*2, l, mid);
build(u*2+1, mid+1, r);
mg(u);
}
void update(int u, int l, int r){
if(l == r){
setnd(u, l);
return;
}
int mid = (l + r) / 2;
if(L <= mid) update(u*2, l, mid);
else update(u*2+1, mid+1, r);
mg(u);
}
int query1(int u, int l, int r){
if(L <= l && r <= R) return mx[u];
int mid = (l + r) / 2, res = 0;
if(L <= mid) res = max(res, query1(u*2, l, mid));
if(mid < R) res = max(res, query1(u*2+1, mid+1, r));
return res;
}
int query2(int u, int l, int r){
if(L <= l && r <= R) return mn[u];
int mid = (l + r) / 2, res = n+1;
if(L <= mid){
int mnp = query2(u*2, l, mid);
if(a[mnp] <= a[res]) swap(mnp, res);
}
if(mid < R){
int mnp = query2(u*2+1, mid+1, r);
if(a[mnp] <= a[res]) swap(mnp, res);
}
return res;
}
ll query3(int u, int l, int r){
if(L <= l && r <= R) return s[u];
int mid = (l + r) / 2; ll res = 0;
if(L <= mid) res += query3(u*2, l, mid);
if(mid < R) res += query3(u*2+1, mid+1, r);
return res;
}
inline void upd(int p){
L = p;
update(1, 1, n);
}
inline int qry1(int p1, int p2){
L = p1; R = p2;
return query1(1, 1, n);
}
inline int qry2(int p1, int p2){
L = p1; R = p2;
return query2(1, 1, n);
}
inline ll qry3(int p1, int p2){
L = p1; R = p2;
return query3(1, 1, n);
}
} d;
inline void delnum(int i){
if(a[i] <= B) c[a[i]].upd(i, -1);
}
inline void addnum(int i){
if(a[i] <= B) c[a[i]].upd(i, 1);
}
void add_ls(int l, int r){
if(l > r) return;
int res = d.qry2(l, r);
if(a[res] == inf) return;
pq.push({l, r, res});
}
ll qry(int l, int r){
ll s = 0; int mx = d.qry1(l, r); bool fg = 0;
for(int i = 1; i <= B; i++){
if(s+1 < i) return s+1;
s += ((ll)c[i].qry(r) - c[i].qry(l-1))*i;
if(s >= mx) return d.qry3(l, r)+1;
}
while(pq.size()) pq.pop();
add_ls(l, r);
while(pq.size()){
piii tp = pq.top();
pq.pop();
if(s+1 < a[tp.mn]) return s+1;
s += a[tp.mn];
if(s >= mx) return d.qry3(l, r)+1;
add_ls(tp.l, tp.mn-1);
add_ls(tp.mn+1, tp.r);
}
return s+1;
}
int main(){
cin >> n >> q;
for(int i = 1; i <= n; i++){
cin >> a[i];
addnum(i);
}
a[n+1] = inf;
d.build(1, 1, n);
while(q--){
cin >> op >> x >> y;
if(op == 1){
delnum(x);
a[x] = y;
addnum(x);
d.upd(x);
}
else{
cout << qry(x, y) << '\n';
}
}
return 0;
}
:::
不知名优化
观察到测试点只有
算法 3
我会正解!
观察到如果
先考虑序列
-
- 此时
2,3 均满足x+1 \ge y ,于是都可以加入。
然后扩展一下可以发现,我们可以将所有的
也就是
然后对于每一组分别建一棵可以区间查最小值和求区间和的线段树,然后扫一遍所有线段树。
- 设查得的最小值为
m ,和为s 。 - 若
x+1 \ge m ,令x \leftarrow x+s - 若
x+1 < m 且区间内的值不是空的,结束并输出x+1 。
为什么这个是对的呢?
设当前的线段树为
- 当
b=0 时,1+x=1 \ge 1 -
-
时间复杂度为
:::success[参考代码]
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10, M = 20, B = 17, V = (1 << 18) + 10, inf = 1e9+1;
int n, q, a[N], g[V], op, x, y;
ll ans = 0;
struct Tree{
int mn[N*4], L, R, id; ll s[N*4];
inline void setnd(int & u, int & i){
if(g[a[i]] == id) mn[u] = s[u] = a[i];
else mn[u] = inf, s[u] = 0;
}
inline void mg(int & u){
s[u] = s[u*2] + s[u*2+1];
mn[u] = min(mn[u*2], mn[u*2+1]);
}
void build(int u, int l, int r){
if(l == r){
setnd(u, l);
return;
}
int mid = (l + r) / 2;
build(u*2, l, mid);
build(u*2+1, mid+1, r);
mg(u);
}
void update(int u, int l, int r){
if(l == r){
setnd(u, l);
return;
}
int mid = (l + r) / 2;
if(L <= mid) update(u*2, l, mid);
else update(u*2+1, mid+1, r);
mg(u);
}
int query1(int u, int l, int r){
if(L <= l && r <= R) return mn[u];
int mid = (l + r) / 2, res = inf;
if(L <= mid) res = min(res, query1(u*2, l, mid));
if(mid < R) res = min(res, query1(u*2+1, mid+1, r));
return res;
}
ll query2(int u, int l, int r){
if(L <= l && r <= R) return s[u];
int mid = (l + r) / 2; ll res = 0;
if(L <= mid) res += query2(u*2, l, mid);
if(mid < R) res += query2(u*2+1, mid+1, r);
return res;
}
inline void upd(int p){
L = p;
update(1, 1, n);
}
inline int qry1(int p1, int p2){
L = p1; R = p2;
return query1(1, 1, n);
}
inline ll qry2(int p1, int p2){
L = p1; R = p2;
return query2(1, 1, n);
}
} t[M];
int main(){
cin >> n >> q;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 0; i <= B; i++){
for(int j = (1 << i); j < (1 << (i+1)); j++) g[j] = i;
}
for(int i = 0; i <= B; i++){
t[i].id = i; t[i].build(1, 1, n);
}
while(q--){
cin >> op >> x >> y;
if(op == 1){
swap(a[x], y);
t[g[y]].upd(x);
t[g[a[x]]].upd(x);
}
else{
ll s = 0;
for(int i = 0; i <= B; i++){
if(s+1 < t[i].qry1(x, y)) break;
s += t[i].qry2(x, y);
}
cout << s+1 << '\n';
}
}
return 0;
}
:::