坑太大了,一时甚至填不起来(现在只填到分块)
分块和莫队都是非常好的暴力算法,
能够让人在面对一些要求维护特殊序列的题时,可以拿到暴力高分甚至AC
甚至还多次出现在各地省选中,学了不亏的好东西
(但是还是要追求巧妙正解啊歪)
分块
分块,顾名思义,将数列切块,批量每个块上的答案信息,
询问时如果询问区间覆盖了这个块,直接统计答案信息就行了,如果没有,就把这个块暴力拆开,查询里面的具体数据。
修改时如果修改区间覆盖了这个块,给这个块打个标记,查询的时候再把标记处理给块内具体数据,如果不覆盖,就暴力拆开维护具体数据。
具体来看题。
Luogu P3870 [TJOI2009]开关
-
有一个01序列,要求支持区间异或,区间查询的操作,强制在线。
-
操作次数 = 序列长度 = 1e5
线段树,分块,朴素暴力都可以A
首先求出块长,然后求出块数,接着处理每个块的基本信息,然后小心维护就行了
分块细节很多,我调了一下午,这里说几个蒟蒻错了的地方
-
打tag的时候已经处理了答案,消tag时又处理了一遍答案......
-
没有处理区间在一个块内的情况
-
循环的时候i就是块的下标,结果循环里面对下标又求了一次下标......
#include<bits/stdc++.h>
using namespace std ;
const int MAXN = 100010;
int n,m;
struct Block{
int l,r;
int ans,tag;
}blo[1000];
int fab[MAXN],a[MAXN],sq,bctr;
void set_up(){
sq = sqrt(n);
bctr = n/sq;
if(n%sq) ++bctr;
// cout<<sq<<"<-sq bctr->"<<bctr<<endl;
for(int i=1;i<=n;i++){
fab[i] = (i-1)/sq+1;
// cout<<fab[i]<<" ";
}
// cout<<endl;
for(int i=1;i<bctr;i++){
blo[i].l = (i-1) * sq + 1;
blo[i].r = i * sq;
}
blo[bctr].l = (bctr-1) * sq + 1;
blo[bctr].r = n;
// for(int i=1;i<=bctr;i++){
// cout<<"i = "<<i;
// cout<<" ans = "<<blo[i].ans;
// cout<<" tag = "<<blo[i].tag;
// cout<<" l = "<<blo[i].l<<" r = "<<blo[i].r<<endl;
// }
}
void pushdown(int x){//无脑push
// if(blo[x].tag == 0) cout<<"???"<<endl;
for(int i=blo[x].l;i<=blo[x].r;i++)
a[i] ^= 1;
blo[x].tag = 0;
}
void change (int x,int y){
if(fab[x] == fab[y]){
if(blo[fab[x]].tag) pushdown(fab[x]);
for(int i=x;i<=y;i++){
a[i] ^= 1;
blo[fab[i]].ans += ((a[i] == 1) ? 1 : -1);
}
return;
}
/*-----------same block--------*/
if(blo[fab[x]].l != x){
if(blo[fab[x]].tag) pushdown(fab[x]);
for(int i=x;i<=blo[fab[x]].r;i++){
a[i] ^= 1;
blo[fab[i]].ans += (a[i] == 1) ? 1 : -1;
}
}
else {
blo[fab[x]].tag ^= 1;
blo[fab[x]].ans = (blo[fab[x]].r - blo[fab[x]].l + 1) - blo[fab[x]].ans;
}
/*-----------left part---------*/
if(blo[fab[y]].r != y){
if(blo[fab[y]].tag) pushdown(fab[y]);
for(int i=blo[fab[y]].l;i<=y;i++){
a[i] ^= 1;
blo[fab[y]].ans += (a[i] == 1) ? 1 : -1;
}
}
else{
blo[fab[y]].tag ^= 1;
blo[fab[y]].ans = (blo[fab[y]].r - blo[fab[y]].l + 1) - blo[fab[y]].ans;
}
/*-----------right part---------*/
for(int i=fab[x]+1;i<=fab[y]-1;i++){
blo[i].tag ^= 1;
blo[i].ans = (blo[i].r - blo[i].l + 1) - blo[i].ans;
}
// for(int i=1;i<=bctr;i++){
// cout<<blo[i].ans<<" ";
// }
return;
/*-----------middle part--------*/
}
int ask(int x,int y){
int ret = 0;
// for(int i=1;i<=bctr;i++){
// cout<<blo[i].ans<<" ";
// }
if(fab[x] == fab[y]){
if(blo[fab[x]].tag) pushdown(fab[x]);
for(int i=x;i<=y;i++){
ret += a[i];
}
return ret;
}
if(x != blo[fab[x]].l) {
if(blo[fab[x]].tag) pushdown(fab[x]);
for(int i = x; i <= blo[fab[x]].r; i++) {
ret += a[i];
}
}
else ret += blo[fab[x]].ans;
// for(int i=1;i<=bctr;i++){
// cout<<blo[i].ans<<" ";
// }
if(y != blo[fab[y]].r) {
if(blo[fab[y]].tag) pushdown(fab[y]);
for(int i = blo[fab[y]].l; i <= y; i++) {
ret += a[i];
}
}
else ret += blo[fab[y]].ans;
// for(int i=1;i<=bctr;i++){
// cout<<blo[i].ans<<" ";
// }
for(int i=fab[x]+1;i<=fab[y]-1;i++){
ret += blo[i].ans;
// cout<<"ret = "<<ret<<endl;
// cout<<"i = "<<i<<endl;
// cout<<"blo[i].ans = "<<blo[i].ans<<endl;
}
return ret;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
set_up();
for(int i=1;i<=m;i++){
int t,x,y;
cin>>t>>x>>y;
if(t == 0){
change(x,y);
}
if(t == 1){
cout<<ask(x,y)<<endl;
}
// cout<<"for this round,the ans1 and ans2 and ans3 is "<<blo[1].ans<<" "<<blo[2].ans<<" "<<blo[3].ans<<endl;
}
return 0;
}
百万丢人调试信息
Luogu P4879 ycz的妹子
这个题题面有坑,而且样例还查不出来,就很xie。
大概概括一下,给你一个序列,要求支持单点修改,查询前缀和为x的位置,序列总和查询
但是实际上题意要稍微复杂一点,建议去看原题
分块搞,前缀和也是整块处理,散块暴力,弱菜的我这次又调了一个下午
#include<bits/stdc++.h>
using namespace std ;
const int MAXN = 500010;
struct Block{
int l,r,ctr;
long long ans;
}blo[1010];
int blen,bctr,fab[MAXN],n,m;
bool book[MAXN];
long long a[MAXN];
struct Ord{
char typ;
int x;
long long y;
}ord[MAXN];
void set_up(){
blen = sqrt(n);
bctr = n/blen;
if(n%blen) bctr++;
for(int i=1;i<=n;i++){
fab[i] = (i - 1) / blen + 1;
}
for(int i=1;i<=n;i++){
blo[fab[i]].ans += a[i];
}
for(int i=1;i<bctr;i++){
blo[i].l = (i - 1) * blen + 1;
blo[i].r = i * blen;
}
blo[bctr].l = (bctr - 1) * blen + 1;
blo[bctr].r = n;
for(int i=1;i<=n;i++){
if(book[i] == true) blo[fab[i]].ctr++;
else break;
}
return;
}
void add(int x,long long y){
a[x] += y;
blo[fab[x]].ans += y;
return;
}
int get_pos(int x){
int ctr=0,ret = 0;
for(int i=1;i<=bctr;i++){
ctr += blo[i].ctr;
if(ctr>=x){
ctr -= blo[i].ctr;
ret = blo[i].l;
break;
}
}
while(1){
ctr += book[ret];
if(ctr == x) break;
ret++;
}
return ret;
}
void del(int x){
int tp = get_pos(x);
blo[fab[tp]].ans -= a[tp];
a[tp] = 0;
if(book[tp] == true) --blo[fab[tp]].ctr;
book[tp] = false;
return;
}
void change(int x,int y){
if(book[x] == false){
book[x] = true;
blo[fab[x]].ctr++;
}
blo[fab[x]].ans -= a[x];
a[x] = y;
blo[fab[x]].ans += a[x];
return;
}
long long ask(){
long long ret = 0;
for(int i=1;i<=bctr;i++){
ret += blo[i].ans;
}
return ret;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
book[i] = true;
}
for(int i=1;i<=m;++i){
cin>>ord[i].typ;
if(ord[i].typ == 'C'){
cin>>ord[i].x>>ord[i].y;
}
if(ord[i].typ == 'D'){
cin>>ord[i].x;
}
if(ord[i].typ == 'I'){
cin>>ord[i].x>>ord[i].y;
n=max(n,ord[i].x);
}
}
set_up();
for(int i=1;i<=m;i++){
if(ord[i].typ == 'Q'){
cout<<ask()<<endl;
}
if(ord[i].typ == 'C'){
add(ord[i].x,-ord[i].y);
}
if(ord[i].typ == 'D'){
del(ord[i].x);
}
if(ord[i].typ == 'I'){
change(ord[i].x,ord[i].y);
}
}
return 0;
}
我好菜啊,不过这个打的挺好看的