带修改莫队的板子题w
似乎还没有介绍过莫队是啥……莫队可是一种号称能解决一切序列问题的算法!以前国家队队长莫涛的名字命名。莫队是一种“优雅的暴力”,他的基本思想是分块,之后离线把询问分别在各个块内按照左右端点排序,用两个指针不断的扫,直到与目标区间重合。这样的话,左指针最多移动n次,而右指针最多移动O(nsqrtn)次,所以莫队的复杂度是O(nsqrtn)的。
莫队的一个优点是特别好写!我们来看下这道例题:小B的询问。
你们知道写了莫队不sort是有多智障吗……
直接看代码即可。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') #define bel(x) ((x-1) / B + 1) using namespace std; typedef long long ll; const int M = 50005; const int B = ceil(sqrt(M)); int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct query { int l,r,id; bool operator < (const query &g) const { if(bel(l) != bel(g.l)) return l < g.l; if(bel(r) != bel(g.r)) return r < g.r; return id < g.id; } }q[M]; int n,m,k,cnt[M],a[M],pl = 1,pr,cur,ans[M]; int po(int x) { return x * x; } void add(int x) { cur += po(cnt[a[x]]+1) - po(cnt[a[x]]); cnt[a[x]]++; } void del(int x) { cur -= po(cnt[a[x]]) - po(cnt[a[x]] - 1); cnt[a[x]]--; } int main() { n = read(),m = read(),k = read(); rep(i,1,n) a[i] = read(); rep(i,1,m) q[i].l = read(),q[i].r = read(),q[i].id = i; sort(q+1,q+1+m); rep(i,1,m) { while(pl > q[i].l) add(--pl); while(pr < q[i].r) add(++pr); while(pl < q[i].l) del(pl++); while(pr > q[i].r) del(pr--); ans[q[i].id] = cur; } rep(i,1,m) printf("%d ",ans[i]); return 0; }
是不是炒鸡简单呀?
带修改莫队与之的思想类似,只是多了一维时间。我们还是把所有的询问按照上述做法排序,然后相同的按照时间顺序排。在每次修改的时候,我们先跳到这次修改的时间的位置,把路径上所有时间早于它的操作加上,晚于他的操作删去即可。注意的是我们在第一次加上这些操作的时候还需要记录这些地方原来是什么颜色,这样回来的时候才能恢复……
其余的就和普通莫队完全一样啦!不过带修改的莫队的块大小要开到n的2/3次方,具体的数学证明……我也不会了。
其实很bug的一点是你可以自己改一改块的大小,然后啥时候他的复杂度是O(你觉得能过),就交上去试试(当然这样很不严谨,还是严格取比较好)
看一下代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') #define bel(x) ((x-1) / B + 1) using namespace std; typedef long long ll; const int M = 100005; const int N = 1000005,B = 2362; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int n,m,pl = 1,pr = 0,cur,res,ans[M],a[M],cnt[N],cntq,cntc,tim[M],pos[M],val[M],pre[M]; char s[5]; struct ask { int id,ti,l,r; bool operator < (const ask &g) const { if(bel(l) != bel(g.l)) return l < g.l; if(bel(r) != bel(g.r)) return r < g.r; return id < g.id; } }q[M]; void cadd(int x) { if(pos[x] >= pl && pos[x] <= pr) { cnt[a[pos[x]]]--; if(!cnt[a[pos[x]]]) res--; } pre[x] = a[pos[x]],a[pos[x]] = val[x]; if(pos[x] >= pl && pos[x] <= pr) { if(!cnt[a[pos[x]]]) res++; cnt[a[pos[x]]]++; } } void cdel(int x) { if(pos[x] >= pl && pos[x] <= pr) { cnt[a[pos[x]]]--; if(!cnt[a[pos[x]]]) res--; } a[pos[x]] = pre[x]; if(pos[x] >= pl && pos[x] <= pr) { if(!cnt[a[pos[x]]]) res++; cnt[a[pos[x]]]++; } } void change(int x) { while(cur < cntc && tim[cur+1] <= x) cadd(++cur); while(cur && tim[cur] > x) cdel(cur--); } void add(int x) { if(!cnt[a[x]]) res++; cnt[a[x]]++; } void del(int x) { cnt[a[x]]--; if(!cnt[a[x]]) res--; } int main() { n = read(),m = read(); rep(i,1,n) a[i] = read(); rep(i,1,m) { scanf("%s",s); if(s[0] == 'Q') { cntq++; q[cntq].id = cntq,q[cntq].ti = i,q[cntq].l = read(),q[cntq].r = read(); } else tim[++cntc] = i,pos[cntc] = read(),val[cntc] = read(); } sort(q+1,q+1+cntq); rep(i,1,cntq) { change(q[i].ti); while(pl > q[i].l) add(--pl); while(pr < q[i].r) add(++pr); while(pl < q[i].l) del(pl++); while(pr > q[i].r) del(pr--); ans[q[i].id] = res; } rep(i,1,cntq) printf("%d ",ans[i]); return 0; }