14____数列分块
14.1____数列分块1(区间加法,单点查询)
题解:
这个题就是数列分块的板子题,对区间做区间修改,单点查询,区间修改为区间加法,线段树同样可以做,但是代码就很复杂了,我们用 tag 来维护分块区间的相同和
在update的时候,如果在同一区间内,那么我们直接 (O(n))解决 ,如果不在的话,那么位于左端和右端的区间,我们都直接 (O(n)) 处理, 在中间的区间,我们直接同一加到 tag
上,这 tag
数组有点像 线段树的 lazy
标记,但是不会 push_down
,查询的时候,我们直接叠加上去就可以了。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 10;
int a[N],s[N],tag[N],len,id[N];
void add(int l,int r,int c)
{
int sid = id[l] , eid = id[r];
if( sid == eid ){
for(int i = l ;i <= r; i++){
a[i] += c, s[sid] += c;
}
return ;
}
for(int i = l ; id[i] == sid ; i++) a[i] += c,s[ sid ] += c;
for(int i = sid + 1 ; i < eid ; i++) tag[i] += c,s[ i ] += c * len;
for(int i = r ; id[i] == eid ; i--) a[i] += c,s[eid] += c;
}
int query(int x)
{
return a[x] + tag[ id[x] ];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n;
cin >> n;
len = sqrt(n);
for(int i = 1; i <= n ; i++){
cin >> a[i];
id[i] = (i - 1) / len + 1;
s[ id[i] ] += a[i];
}
for(int i = 0 ; i < n ; i++){
int op,x,y,z;
cin >> op >> x >> y >> z;
if( op == 0 ){
add(x,y,z);
}else{
cout << query(y) << endl;
}
}
return 0;
}
14.2____数列分块2
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int a[N],len,id[N],tag[N],vec[N];
/// id * len 当前块最后一个元素
/// (id - 1) * len + 1 当前块的第一个元素
/// 注意lower_bound 是左闭右开 (取不到第二个值)
/// 开区间使用符号小括号()表示,闭区间使用符号中括号[]表示
void reset(int tes,int tee) /// x为所在分块的编号
{
for(int i = tes ; i <= tee ; i++) vec[i] = a[i];
sort(vec + tes,vec + tee + 1);
}
void update(int l,int r,int c)
{
int sid = id[l],eid = id[r];
if(sid == eid){
for(int i = l ; i <= r; i++) a[i] += c;
reset( (sid - 1)*len +1 ,sid *len );
return ;
}
///
for(int i = l; id[i] == sid ; i++) a[i] += c;
reset( (sid - 1)*len +1 ,sid *len );
for(int i = sid + 1 ; i < eid ; i++) tag[i] += c;
for(int i = r ; id[i] == eid ; i--) a[i] += c;
reset( (eid - 1)*len +1 ,eid *len );
}
int query(int l,int r,int x)
{
int sid = id[l], eid = id[r] , cnt = 0;
if( sid == eid){
for(int i = l ; i <= r; i++)
if( a[i] + tag[ id[i] ] < x ) cnt++;
return cnt;
}
///
int te = sid * len ;
for(int i = l ; i <= te ; i++)
if( a[i] + tag[ sid ] < x ) cnt ++;
for(int i = sid + 1; i < eid ; i++){
int tes = (i - 1)*len + 1, tee = i *len;
cnt += int( lower_bound( vec + tes , vec + tee + 1, x - tag[i]) - vec - tes );
}
te = (eid - 1) * len + 1;
for(int i = te ; i <= r; i++)
if( a[i] + tag[ eid ] < x ) cnt++;
return cnt;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n;
cin >> n;
len = sqrt(n);
for(int i = 1; i <= n ; i++){
cin >> a[i];
id[i] = (i - 1) /len + 1;
}
for(int i = 1 ; i <= id[n] ; i++ ){
reset((i - 1)*len + 1, i * len);
/// 注意如果是最后一个分块,要单独弄
if (i == id[n]) {
reset((i - 1)*len + 1, n);
}
}
for(int i = 0; i < n ; i++){
int op,x,y,z;
cin >> op >> x >> y >> z;
if( op == 0 ){
update(x,y,z);
}else{
cout << query(x,y,z*z) <<endl;
}
}
return 0;
}
线段树
#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs (rt<<1|1)
#define Mid ((T[rt].l + T[rt].r)>>1)
#define L ( T[rt].l)
#define R (T[rt].r)
using namespace std;
const int N = 5e4 + 10;
void file() {
#ifdef ONLINE_JUDGE
#else
freopen("d:/workProgram/test.in", "r", stdin);
#endif
}
struct Node {
int l, r;
int ma, mi;
int lazy ;
} T[N << 2];
int a[N];
void up(int rt) {
T[rt].ma = max(T[ls].ma, T[rs].ma);
T[rt].mi = min(T[ls].mi, T[rs].mi);
}
void down(int rt) {
if (T[rt].lazy != 0) {
T[ls].lazy += T[rt].lazy;
T[rs].lazy += T[rt].lazy;
T[ls].ma += T[rt].lazy;
T[rs].ma += T[rt].lazy;
T[ls].mi += T[rt].lazy;
T[rs].mi += T[rt].lazy;
T[rt].lazy = 0;
}
}
void build(int rt, int l, int r) {
T[rt] = {l, r, 0, 0, 0};
if (l == r) {
T[rt].ma = T[rt].mi = a[l];
return ;
}
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
up(rt);
}
void rangeUpdate(int rt, int l, int r, int val) {
if (l <= L && r >= R) {
T[rt].ma += val;
T[rt].mi += val;
T[rt].lazy += val;
return ;
}
down(rt);
if (l <= Mid)
rangeUpdate(ls, l, r, val);
if (r > Mid)
rangeUpdate(rs, l, r, val);
up(rt);
}
int rangeQuery(int rt, int l, int r, int x) {
if (T[rt].mi >= x)
return 0;
if (l <= L && r >= R && T[rt].ma < x) {
return T[rt].r - T[rt].l + 1;
}
down(rt);
int cnt = 0;
if (l <= Mid)
cnt += rangeQuery(ls, l, r, x);
if (r > Mid)
cnt += rangeQuery(rs, l, r, x);
return cnt;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
//file();
int n;
cin >> n;
for (int i = 1 ; i <= n ; i++) {
cin >> a[i];
}
build(1, 1, n);
for (int i = 0; i < n ; i++) {
int op, l, r, x;
cin >> op >> l >> r >> x;
//cout << "--"<< i <<endl;
if (op == 0) {
rangeUpdate(1, l, r, x);
} else {
cout << rangeQuery(1, l, r, x * x) << endl;
}
}
//system("pause");
return 0;
}
14.3____数列分块3(求区间中比数x,小的第一个数)
线段树 TLE了...可能是维护最大值和最小值,必须要递归到最深才可以更新答案,他数据全是查询,就把线段树卡死了...
还是贴一下代码,我看了统计,前面的没有一个是线段树
#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs (rt<<1|1)
#define Mid ( (T[rt].l + T[rt].r) >> 1)
#define L (T[rt].l)
#define R (T[rt].r)
#define INF (-1)*0x3f3f3f3f3f
using namespace std;
void file()
{
#ifdef ONLINE_JUDGE
#else
freopen( "d:/workProgram/test.in","r",stdin );
#endif
}
typedef long long ll;
const ll N = 1e5 + 10;
struct Node
{
ll l,r;
ll ma,mi,lazy;
}T[N << 2];
ll a[N],ans; /// ans = -1*(0x3f3f3f3f);
void push_up(ll rt)
{
T[rt].ma = max( T[ls].ma , T[rs].ma );
T[rt].mi = min( T[ls].mi , T[rs].mi );
}
void push_down(ll rt)
{
if( T[rt].lazy != 0 ){
T[ls].ma += T[rt].lazy;
T[rs].ma += T[rt].lazy;
T[ls].mi += T[rt].lazy;
T[rs].mi += T[rt].lazy;
T[ls].lazy += T[rt].lazy;
T[rs].lazy += T[rt].lazy;
T[rt].lazy = 0;
}
}
void build(ll rt,ll l,ll r)
{
T[rt] = {l,r,0,0,0};
if(l == r){
T[rt].ma = T[rt].mi = a[l];
//cout << "LL:" << l << "a:" << a[l] <<endl;
return;
}
build(ls,l,Mid),build(rs,Mid+1,r);
push_up(rt);
//cout<< "l:" << L << "R:" << R << "ma:" << T[rt].ma <<endl;
}
void update(ll rt,ll l,ll r,ll val)
{
if( l <= L && r >= R ){
T[rt].ma += val;
T[rt].mi += val;
T[rt].lazy += val;
return ;
}
push_down(rt);
if( l <= Mid )update(ls,l,r,val);
if( r > Mid) update(rs,l,r,val);
push_up(rt);
}
void query(ll rt,ll l,ll r,ll val)
{
if( T[rt].mi > val || T[rt].ma < ans) return ;
if( l <= L && r >= R && T[rt].ma < val ){
ans = max( ans , T[rt].ma);
return ;
}
if( l <= L && r >= R && L == R && T[rt].ma >= val ) return ;
if( R < l || L > r || L == R) return ;
//cout<< "l:" << L << "R:" << R << "ma:" << T[rt].ma <<endl;
push_down(rt);
if( l <= Mid ) query(ls,l,r,val);
if( r >= Mid ) query(rs,l,r,val);
push_up(rt);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
//file();
ll n;
cin >> n;
for(int i = 1; i <= n ;i++){
cin >> a[i];
}
build(1,1,n);
for(int i = 0 ; i < n ;i++){
int op,x,y,z;
cin >> op >> x >> y >> z;
if( op == 0 ){
update(1,x,y,z);
}else{
ans = INF;
query(1,x,y,z);
//cout << "ans:" << ans << endl;
if( ans == INF ){
cout << -1 <<endl;
}else{
cout << ans <<endl;
}
}
}
return 0;
}
14.5____数列分块5(区间开方,区间求和)
我的自己的做法是,开两个数组来维护,sum[]
维护区间和,num[]
来维护区间开了几次方
num[]
的由来是因为 $$2^{32}$$ 大小的数,最多开7次就到1了,之后就没必要开了,所以在add
中,对于一个整块如果 num[i] > 7 || sum[i] <= len
那么我们对这个块就不需要进行操作了
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N], sum[N], num[N], id[N], len;
void down(int l, int r) {
for (int i = l; i <= r; i++)
a[i] = sqrt(a[i]);
}
int get(int l, int r) {
int res = 0 ;
for (int i = l ; i <= r; i++)
res += a[i];
return res;
}
void update(int l, int r) {
int L = id[l], R = id[r];
if (L == R) {
for (int i = l ; i <= r; i++)
a[i] = sqrt(a[i]);
return ;
}
for (int i = l ; id[i] == L ; i++)
a[i] = sqrt(a[i]);
for (int i = r ; id[i] == R ; i--)
a[i] = sqrt(a[i]);
sum[L] = get((L - 1) * len + 1, L * len);
sum[R] = get((R - 1) * len + 1, R * len);
for (int i = L + 1; i < R; i ++) {
if (num[i] > 7 || sum[i] <= len)
continue;
else {
down((i - 1)*len + 1, i * len);
num[i]++;
sum[i] = get((i - 1) * len + 1, i * len);
}
}
}
int query(int l, int r) {
int L = id[l], R = id[r], res = 0;
if (L == R) {
for (int i = l ; i <= r ; i++)
res += a[i];
return res;
}
for (int i = l ; id[i] == L ; i++)
res += a[i];
for (int i = r ; id[i] == R ; i--)
res += a[i];
for (int i = L + 1; i < R; i ++)
res += sum[i];
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n;
cin >> n;
len = sqrt(n);
for (int i = 1; i <= n; i++) {
cin >> a[i];
id[i] = (i - 1) / len + 1;
sum[ id[i] ] += a[i];
}
for (int i = 0 ; i < n ; i++) {
int op, x, y, z;
cin >> op >> x >> y >> z;
if (op == 0) {
update(x, y);
} else {
cout << query(x, y) << "
";
}
}
return 0;
}
另一个dalao的做法提交记录 #601465 - LibreOJ (loj.ac)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10, M = 500;
int a[N],ma[M],L[M],R[M],id[N],len,sum[M];
inline void update_sum(int l,int r, int k)
{
for(int i = l ; i <= r; i ++) sum[k] -= a[i], a[i] = sqrt(a[i]),sum[k] += a[i];
}
inline void update_max(int k)
{
ma[k] = sqrt( ma[k] );
for(int i = L[k] ; i <= R[k]; i++) ma[k] = ma[k] > a[i]? ma[k]:a[i];
}
inline void update(int l,int r)
{
if( id[ l ] == id[ r ] ){
if( ma[ id[l] ] <= 1 ) return ;
update_sum(l,r,id[l]);
update_max(id[l]);
return ;
}
if( ma[ id[l] ] > 1 )
update_sum( l,R[id[l]],id[l] ),update_max( id[l] );
if( ma[ id[r] ] > 1 )
update_sum( L[ id[r] ] , r, id[r] ), update_max( id[r] );
for(int i = id[l] + 1 ; i < id[r] ; i++ ){
if( ma[ i ] > 1 ) update_sum( L[i],R[i],i ),ma[i] = sqrt(ma[i]);
}
}
int query(int l,int r)
{
int res = 0;
if( id[l] == id[r] ){
for(int i = l ; i <= r; i ++) res += a[i];
return res;
}
for(int i = l ; i <= R[ id[l] ] ; i ++) res += a[i];
for(int i = L[ id[r] ] ; i <= r; i ++) res += a[i];
for(int i = id[ l ] + 1 ; i < id[r] ; i++) res += sum[i];
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n;
cin >> n ;
len = sqrt(n);
for(int i = 1; i <= n ; i++){
cin >> a[i];
id[i] = ( i - 1 ) / len + 1;
sum[ id[i] ] += a[i];
ma[ id[i] ] = ma[ id[i] ] > a[i] ? ma[ id[i] ] : a[i];
}
for(int i = 1 ; i <= id[n] ; i++){
L[i] = (i-1)*len+1 , R[i] = min( i*len , n );
}
for(int i = 0; i < n ; i++){
int op,x,y,z;
cin >> op >> x >> y >>z;
if( op == 0 ){
update( x,y );
} else {
cout << query( x,y ) <<endl;
}
}
return 0;
}