CF431E (权值线段树, 线段树上二分,动态开点)
https://www.luogu.com.cn/problem/CF431E
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define int long long
const int N = 1e5 + 5;
const int M = 1e6 + 5;
const int mod = 998244353;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
struct SegTree {
int lc, rc, cnt; ll sum;
} tree[N * 40 + 5];
int a[N], root, cur, sz;
void modify( ll pos, ll x, ll l, ll r, int & now) {
if(!now) {
now = ++ sz;
}
if(l == r) {
tree[now].sum += x * pos; tree[now].cnt += x; return;
}
ll mid = l + r >> 1;
if(pos <= mid) modify(pos, x, l, mid, tree[now].lc);
else modify( pos, x, mid + 1, r,tree[now].rc);
//pushup
tree[now].sum = tree[tree[now].lc].sum + tree[tree[now].rc].sum;
tree[now].cnt = tree[tree[now].lc].cnt + tree[tree[now].rc].cnt;
}
double query( ll val, ll l, ll r, int now) { // 访问到没有开的点 lc的cnt为0,会一直往大的跑,跑到一个大值能满足
ll pcnt = 0, psum = 0;
while(l != r) {
ll mid = l + r >> 1;
double sum = 1.0 * mid * (tree[tree[now].lc].cnt + pcnt) - tree[tree[now].lc].sum - psum; //乘法都可能爆longlong
//加到mid(不包括mid)每柱加的不超过mid能加多少
if( sum <= val ) l = mid + 1, pcnt += tree[tree[now].lc].cnt, psum += tree[tree[now].lc].sum, now = tree[now].rc;
else r = mid, now = tree[now].lc;
}
// 最终能填满k的高度,l和r在k + 1处相等,答案就是val删去前面填满k的,给k个柱再平均分配剩余的
return 1.0 *(val - 1.0 * (l - 1) * (pcnt ) + psum ) / pcnt + l - 1;
}
signed main() {
int n, q; cin >> n >> q;
ll limit = 1e9; // 水银值域
for ( int i = 1; i <= n; ++ i ) {
cin >> a[i]; modify( a[i], 1,0, limit, root);
}
while ( q -- ) {
int opt, p, x;
cin >> opt;
if(opt == 1) {
cin >> p >> x;
modify(a[p], -1, 0, limit, root); //删旧加新
modify(x, 1, 0, limit, root);
a[p] = x;
} else {
ll x; cin >> x;
printf("%.5lf\n", query(x, 0, limit, root));
}
}
return 0;
}