分块9题【参考hzw】

参考hzw大佬的blog

1

给出一个长为n的数列,以及n个操作,操作涉及区间加法,单点查值。

嗯 分块板子啦
贴下主要部分的代码

struct Block{
	int size, cnt, pos[N];
	int atag[N];
	inline int query(int x){
		return a[x] + atag[pos[x]];
	}
	inline void add(int x, int y, int d){
		for(int i = x; i <= min(pos[x] * size, y); ++i)
		    a[i] += d;
		if(pos[x] != pos[y])
		    for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
		        a[i] += d;
		for(int i = pos[x] + 1; i <= pos[y] - 1; ++i)
		    atag[i] += d;
	}
}bl;

2

给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数

考察块内排序的维护

struct Block{
 int size, cnt, pos[N];
 int atag[sqN];
 vector<int> ve[sqN];
 void reset(int x){
  ve[x].clear();
  for(int i = (x - 1) * size + 1; i <= min(x * size, n); ++i){//min(, n)!!!
   ve[x].push_back(a[i]);
  }
  sort(ve[x].begin(), ve[x].end());
 }
 inline int query(int x, int y, int d){
  int res = 0;
  for(int i = x; i <= min(pos[x] * size, y); ++i){
   if(a[i] < d - atag[pos[x]]) ++res;
  }
  if(pos[x] != pos[y])
      for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
          if(a[i] < d - atag[pos[y]]) ++res;
  for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
   res += lower_bound(ve[i].begin(), ve[i].end(), d - atag[i]) - ve[i].begin();
  }
  return res;
 }
 inline void add(int x, int y, int d){
  for(int i = x; i <= min(pos[x] * size, y); ++i)
      a[i] += d;
  reset(pos[x]);
  if(pos[x] != pos[y]){
      for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
          a[i] += d;
      reset(pos[y]); 
  }
  for(int i = pos[x] + 1; i <= pos[y] - 1; ++i)
      atag[i] += d;
 }
}bl;

3

给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的前驱(比其小的最大元素)。

考察维护块内前驱(当然后继同理

struct Block{
 int size, cnt, pos[N];
 int atag[sqN];
 set<int> st[sqN];
 inline int pre(int x, int y, int d){
  int res = -inf;
  set<int> :: iterator it;
  for(int i = x; i <= min(pos[x] * size, y); ++i){
   if(a[i] + atag[pos[x]] < d) res = max(res, a[i] + atag[pos[x]]);
  }
  if(pos[x] != pos[y])
      for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
          if(a[i] + atag[pos[y]] < d) res = max(res, a[i] + atag[pos[y]]);
  for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
      it = st[i].lower_bound(d - atag[i]);
      if(it == st[i].begin()) continue;
      --it;
      res = max(*it + atag[i], res);
  }
  return res == -inf ? -1 : res;
 }
 inline void add(int x, int y, int d){
  for(int i = x; i <= min(pos[x] * size, y); ++i){
   st[pos[x]].erase(a[i]);
      a[i] += d;
   st[pos[x]].insert(a[i]);
  }
  if(pos[x] != pos[y]){
      for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
       st[pos[y]].erase(a[i]);
          a[i] += d;
          st[pos[y]].insert(a[i]);
      }
  }
  for(int i = pos[x] + 1; i <= pos[y] - 1; ++i)
      atag[i] += d;
 }
}bl;

4

给出一个长为n的数列,以及n个操作,操作涉及区间加法,区间求和。

记得开longlong哦

struct Block{
	int size, cnt, pos[N];
    long long atag[sqN], sum[sqN];
	inline long long query(int x, int y, long long d){
		long long res = 0;
		for(int i = x; i <= min(pos[x] * size, y); ++i)
			res += a[i] + atag[pos[x]];
		if(pos[x] != pos[y])
		    for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
		        res += a[i] + atag[pos[y]];
		res %= d; 
		for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
		    res += sum[i] + atag[i] * size;
		    res %= d;
		}
		return res % d;
	}
	inline void add(int x, int y, long long d){
		for(int i = x; i <= min(pos[x] * size, y); ++i){
			sum[pos[x]] += d;
		    a[i] += d;
		}
		if(pos[x] != pos[y]){
		    for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
		    	sum[pos[y]] += d;
		        a[i] += d;
		    }
		}
		for(int i = pos[x] + 1; i <= pos[y] - 1; ++i)
		    atag[i] += d;
	}
}bl;

5

给出一个长为n的数列,以及n个操作,操作涉及区间开方,区间求和。

一个数经过几次开方就会变成0或1
所以维护一下这个块有没有全变成0或1就好啦

struct Block{
	int size, cnt, pos[N];
    long long sum[sqN];
	bool flag[sqN];
	inline long long query(int x, int y){
		long long res = 0;
		for(int i = x; i <= min(pos[x] * size, y); ++i)
			res += a[i];
		if(pos[x] != pos[y])
		    for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
		        res += a[i];
		for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
		    res += sum[i];
		}
		return res;
	}
	inline bool sq(int x){
		bool ret = 1;
		for(int i = (x - 1) * size + 1; i <= min(n, x * size); ++i){
			a[i] = sqrt(a[i]);
			if(ret && a[i] != 0 && a[i] != 1) ret = 0; 
		}
		sum[x] = 0;
		for(int i = (x - 1) * size + 1; i <= min(n, x * size); ++i){
			sum[x] += a[i];
		}
		return ret;
	}
	inline void modify(int x, int y){
		for(int i = x; i <= min(pos[x] * size, y); ++i){
			sum[pos[x]] -= a[i];
		    a[i] = sqrt(a[i]);
		    sum[pos[x]] += a[i];
		}
		if(pos[x] != pos[y]){
		    for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
				sum[pos[y]] -= a[i];
			    a[i] = sqrt(a[i]);
			    sum[pos[y]] += a[i];
		    }
		}
		for(int i = pos[x] + 1; i <= pos[y] - 1; ++i)
		    if(!flag[i]) flag[i] = sq(i);
	}
}bl;

6

给出一个长为n的数列,以及n个操作,操作涉及单点插入,单点询问,数据随机生成。

本题随机生成数据 用链表什么的直接插就行
不随机就要重构 有三种做法
1.每根号n次插入后,重新把数列平均分一下块
2.当某个块过大时重构
3.把特别大的块分成两半

struct Block{
	int size, cnt, pos[N];
	int stk[N], top;
	vector<int> ve[sqN];
	pair<int, int> query(int x){
		int i;
		for(i = 1; x > ve[i].size(); ++i)
		    x -= ve[i].size();
		return make_pair(i, x - 1);
	}
	void rebuild(){
		top = 0;
		for(int i = 1; i <= cnt; ++i){
			for(vector<int> :: iterator j = ve[i].begin(); j != ve[i].end(); ++j){
				stk[++top] = *j;
			}
			ve[i].clear();
		}
		size = sqrt(top);
		for(int i = 1; i <= top; ++i){
			pos[i] = (i - 1) / size + 1;//是/不是%!!!!! 
			ve[pos[i]].push_back(stk[i]);
		}
		cnt = pos[top];
	}
	void ins(int x, int y){
		pair<int, int> t = query(x);
		ve[t.first].insert(ve[t.first].begin() + t.second, y);
	}
}bl;

7

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间乘法,区间加法,单点询问。

这道题就是取一个优先级
和洛谷线段树2差不多
longlong竟然比int慢这么多【雾

struct Block{
	int size, cnt, pos[N];
	int atag[N], mtag[N];
	int query(int x){
	    return (a[x] * mtag[pos[x]] + atag[pos[x]]) % P;
	}
	inline void init(){
		for(int i = 1; i <= cnt; ++i){
			mtag[i] = 1;
		}
	}
	inline void reset(int x){
		for(int i = (x - 1) * size + 1; i <= min(x * size, n); ++i){
			a[i] = (a[i] * mtag[x] + atag[x]) % P;
		}
		mtag[x] = 1, atag[x] = 0;
	}
	void add(int x, int y, int d){
		reset(pos[x]);
	    for(int i = x; i <= min(y, pos[x] * size); ++i){
	    	a[i] = (a[i] + d) % P;
	    }
	    if(pos[x] != pos[y]){
	    	reset(pos[y]);
	    	for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
	    		a[i] = (a[i] + d) % P;
	    	}
	    }
	    for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
	    	atag[i] = (atag[i] + d) % P;
	    }
	}
	void mul(int x, int y, int d){
	    reset(pos[x]);
	    for(int i = x; i <= min(y, pos[x] * size); ++i){
	    	a[i] = (a[i] * d) % P;
	    }
	    if(pos[x] != pos[y]){
	    	reset(pos[y]);
	    	for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
	    		a[i] = (a[i] * d) % P;
	    	}
	    }
	    for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
	    	atag[i] = (atag[i] * d) % P;
	    	mtag[i] = (mtag[i] * d) % P;
	    }//注意这里的处理哦 
	}
}bl;

8

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间询问等于一个数 c 的元素,并将这个区间的所有元素改为 c。

哇这一波复杂度证明真心阔以
今天才理解lxl那节课上讲的染色qvq
和开方那道题有一点类似
都是只能一个一个修改的东西
中间的值一样了就标记一下
这样虽然看起来不太可行
但实际上你修改两个端点 最多只能拆两个同色的块
要修改上 √n 个才能把复杂度拉到 O ( n ) O(n) O(n)
(有√n个不同色的块 每个需要√n复杂度维护)
那这么一平摊 又 O ( n √ n ) O(n √n) O(nn)了。。。
懒得打根号

struct Block{
	int size, cnt, pos[N];
	int col[sqN];
	void init(){
		memset(col, -1, sizeof(col));
	}
	void reset(int x){
		if(col[x] == -1) return ;
		for(int i = (x - 1) * size + 1; i <= min(size * x, n); ++i)
		    a[i] = col[x];
		col[x] = -1; 
	}
	int modify(int x, int y, int d){
		int res = 0;
		reset(pos[x]);
	    for(int i = x; i <= min(y, pos[x] * size); ++i){
	    	if(a[i] == d) ++res;
	    	else a[i] = d;
	    }
	    if(pos[x] != pos[y]){
	    	reset(pos[y]);
	    	for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
	    	    if(a[i] == d) ++res;
	    	    else a[i] = d;
	        }
	    }
	    for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
	    	if(~col[i]){
	    		if(col[i] != d) col[i] = d;
	    		else res += size;
	    	}
	    	else {
	    		for(int j = (i - 1) * size + 1; j <= i * size; ++j){
	    			if(a[j] == d) ++res;
	    			else a[j] = d;
	    		}
	    		col[i] = d;
	    	}
	    }
	    return res;
	}
}bl;

9

给出一个长为n的数列,以及n个操作,操作涉及询问区间的最小众数。

忍住没有用莫队写
这是利用了本质的√n大法吧。。。

struct Block{
	int size, cnt, pos[N];
	int colcnt[N];
	void init(int x){
		int mx = 0, res;
		memset(colcnt, 0, sizeof(colcnt));
		for(int i = (x - 1) * size + 1, y; i <= n; ++i){
			y = ls[a[i]]; ++colcnt[y];
 			if(mx < colcnt[y] || (mx == colcnt[y] && val[res] > val[y])){
 				mx = colcnt[y]; res = y;
 			}
 			g[x][pos[i]] = mx;
 			f[x][pos[i]] = res;
		}
	}
	int ve_q(int x, int y, int c){
		return upper_bound(ve[c].begin(), ve[c].end(), y) - lower_bound(ve[c].begin(), ve[c].end(), x);
	}
	int query(int x, int y){
		int res = 0, mx = 0;
		if(pos[x] < pos[y] - 1){
			res = f[pos[x] + 1][pos[y] - 1]; mx = g[pos[x] + 1][pos[y] - 1];
		}
		for(int i = x, t, col; i <= min(y, pos[x] * size); ++i){
			col = ls[a[i]];
			t = ve_q(x, y, col);
			if(t > mx || (t == mx && val[col] < val[res])){
				mx = t, res = col;
			}
		}
		if(pos[y] != pos[x])
		for(int i = (pos[y] - 1) * size + 1, t, col; i <= y; ++i){
			col = ls[a[i]];
			t = ve_q(x, y, col);
			if(t > mx || (t == mx && val[col] < val[res])){
				mx = t, res = col;
			}
		}
	    return res;
	}
}bl;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值