题目链接
题意:这题就是先让你套个公式把n个区间算出来,然后对于单个区间在序列里加上这个区间内所有数,然后再求此时序列的中位数。
题解:中位数实际上就是求整个序列第n/2大,考虑权值线段树,但是因为区间范围太大,所以要对区间进行离散化。
离散化之前,首先要将所有区间的左端点减1。为什么呢?因为离散化后线段树上的每个点存的实际上是一段区间,那么比如区间【3,8】,离散化之前我们要把它变成(2,8】,这样我的3这个点才能代表(2,3】这个区间。
给定两个区间我们来模拟一下 【3,8】,【5,9】 ,离散化后是这样子的
2 4 8 9
1 2 3 4
此时(2,8】 对应的是 【1,3】,(4,9】对应的是【2,4】;
还记得我之前说过的吗,线段树上每一个点代表的是一段区间。
这个体现在哪里,体现在对于每一个区间我都会用s[r] - s[l - 1] 来表示,详情请看代码。
那么在线段树上【1,1】这个点代表的是 s[1] - s[0](0,2】;
【2,2】这个点代表的是s[2] - s[1](2,4】,依次类推下去 3代表的是(4,8】,4代表的是(8,9】。
所以当我要插入【3,8】时,对应的离散化区间不应该是【1,3】,而应该是【1 + 1,3】也就是【2,3】,因为【2,2】覆盖了(2,4】,【3,3】覆盖了(4,8】,所以左端点要往后移一个位置。
这里离散化之前和之后的区间混在一起可能有点绕,不过我觉得我已经讲得很清楚了,如果还有不懂的地方欢迎提问。
#include<bits/stdc++.h>
using namespace std;
#define mid (l + r) / 2
#define ls o * 2
#define rs o * 2 + 1
typedef long long ll;
const int N = 8e5 + 10;
ll sum[N * 4], lazy[N * 4];
ll x[N], y[N], L[N], R[N], s[N];
int n, cnt;
void init()
{
ll a1,b1,c1,m1;
ll a2,b2,c2,m2;
cin>>n;
cin>>x[1]>>x[2]>>a1>>b1>>c1>>m1;
cin>>y[1]>>y[2]>>a2>>b2>>c2>>m2;
for(int i = 1; i <= n; ++i){
if(i > 2){
x[i] = (a1 * x[i - 1] % m1 + b1 * x[i - 2] % m1 + c1 % m1) % m1;
y[i] = (a2 * y[i - 1] % m2 + b2 * y[i - 2] % m2 + c2 % m2) % m2;
}
L[i] = min(x[i], y[i]);//左区间-1
R[i] = max(x[i], y[i]) + 1;
s[++cnt] = L[i];
s[++cnt] = R[i];
}
sort(s + 1, s + 1 + cnt);
cnt = unique(s + 1, s + 1 + cnt) - s - 1;
for(int i = 1; i <= n; ++i){
L[i] = lower_bound(s + 1, s + 1 + cnt, L[i]) - s;
R[i] = lower_bound(s + 1, s + 1 + cnt, R[i]) - s;
}
}
void pu(int o, int l, int r){
if(lazy[o]){
lazy[ls] += lazy[o];
lazy[rs] += lazy[o];
sum[ls] += (s[mid] - s[l - 1]) * lazy[o];
sum[rs] += (s[r] - s[mid]) * lazy[o];
lazy[o] = 0;
}
}
void up(int o, int l, int r, int ql, int qr){
if(ql <= l && qr >= r){
sum[o] += s[r] - s[l - 1];
lazy[o]++;
return;
}
pu(o, l, r);
if(ql <= mid) up(ls, l, mid, ql, qr);
if(qr > mid) up(rs, mid + 1, r, ql, qr);
sum[o] = sum[ls] + sum[rs];
}
ll qu(int o, int l, int r, ll val){
if(l == r){
int tmp = sum[o] / (s[r] - s[l - 1]);//求这段区间内每个数的个数
return (val + tmp - 1) / tmp + s[l - 1];//第一个式子就是向上取整一下
}
pu(o, l, r);
if(val <= sum[ls]) return qu(ls, l, mid, val);//权值线段树求区间第k大
else return qu(rs, mid + 1, r, val - sum[ls]);
}
int main()
{
init();
for(int i = 1; i <= n; ++i){
up(1, 1, cnt, L[i] + 1, R[i]);//左区间+1
ll v = sum[1]/2 + (sum[1] % 2 == 1);//中位数所在位置
printf("%lld\n", qu(1, 1, cnt, v));
}
}