神奇的莫队算法!!!我只能说Qrz,之前一直在一道题上面傻逼了,wa到死,附上链接:http://www.spoj.com/problems/ZQUERY/en/ 个人觉得还是一道蛮不错的题。
莫队简单粗暴,据说可以再o(nlogn)内解决一切无修改的区间查询问题!!!核心应该就是:1:按左端点分块排序。2:能在o(1)的复杂度内解决[L,R] -> [L,R+1],[L,R]->[L,R-1],[L,R] -> [L-1,R],[L,R] -> [L+1,R]所维护的值,当着o(1)视情况而定。只要保证复杂度满足题目条件即可。
莫队另外一个优点就是易于实现!!!好写!!!再次Orz莫涛大神!!!
下面介绍几道入门题:
1:小Z的袜子:http://www.lydsy.com/JudgeOnline/problem.php?id=2038
最基本的题,直接给代码了:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define FOR(i,x,y) for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define ll long long
#define N 50010
using namespace std;
const int M = (int)sqrt(1.0*N)+1;
int n,m;
int cnt[N],col[N];
struct Command{
ll l,r,id;
bool operator < (const Command& rhs) const{
if(l/M == rhs.l/M) return r < rhs.r;
return (l/M) < (rhs.l/M);
}
}cmd[N];
struct Ans{
ll mol,den;
}ans[N];
ll gcd(ll a,ll b){
return b == 0 ? a : gcd(b,a%b);
}
void solve(){
ll L = 1,R = 0;
ll temp = 0;
FOR(i,0,m){
while(R < cmd[i].r) {
R ++;
temp -= cnt[col[R]]*cnt[col[R]];
cnt[col[R]] ++;
temp += cnt[col[R]]*cnt[col[R]];
}
while(R > cmd[i].r){
temp -= cnt[col[R]]*cnt[col[R]];
cnt[col[R]] --;
temp += cnt[col[R]]*cnt[col[R]];
R --;
}
while(L < cmd[i].l){
temp -= cnt[col[L]]*cnt[col[L]];
cnt[col[L]] --;
temp += cnt[col[L]]*cnt[col[L]];
L ++;
}
while(L > cmd[i].l){
L --;
temp -= cnt[col[L]]*cnt[col[L]];
cnt[col[L]] ++;
temp += cnt[col[L]]*cnt[col[L]];
}
ll x = temp - (R-L+1);
ll y = (R-L+1)*(R-L);
if(!x) ans[cmd[i].id].mol = 0,ans[cmd[i].id].den = 1;
else{
ll tem = gcd(x,y);
ans[cmd[i].id].mol = x/tem;
ans[cmd[i].id].den = y/tem;
}
}
}
int main()
{
//freopen("test.in","r",stdin);
while(~scanf("%d%d",&n,&m)){
memset(cnt,0,sizeof(cnt));
FOR(i,1,n+1) scanf("%d",&col[i]);
FOR(i,0,m){
scanf("%lld%lld",&cmd[i].l,&cmd[i].r);
cmd[i].id = i;
}
sort(cmd,cmd+m);
solve();
FOR(i,0,m){
printf("%lld/%lld\n",ans[i].mol,ans[i].den);
}
}
return 0;
}
2:Sona https://ac.2333.moe/Problem/view.xhtml?id=1457
这道题给人一种然并卵的感觉,和上题基本一样
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#define FOR(i,x,y) for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define ll long long
#define N 100100
using namespace std;
const int M = (int)sqrt(1.0*N);
int n,m;
ll cnt[N],col[N];
ll ans[N];
struct Col{
ll note;
int id;
bool operator < (const Col& rhs) const{
return note < rhs.note;
}
}con[N];
struct Commend{
int l,r;
int id;
bool operator < (const Commend& rhs) const{
if(l/M == rhs.l/M) return r < rhs.r;
return (l/M) < (rhs.l/M);
}
}cmd[N];
void solve(){
int L = 1,R = 0;
FOR(i,0,N) cnt[i] = 0;
ll temp = 0;
FOR(i,0,m){
while(R < cmd[i].r){
R ++;
temp -= cnt[col[R]]*cnt[col[R]]*cnt[col[R]];
cnt[col[R]] ++;
temp += cnt[col[R]]*cnt[col[R]]*cnt[col[R]];
}
while(R > cmd[i].r){
temp -= cnt[col[R]]*cnt[col[R]]*cnt[col[R]];
cnt[col[R]] --;
temp += cnt[col[R]]*cnt[col[R]]*cnt[col[R]];
R --;
}
while(L < cmd[i].l){
temp -= cnt[col[L]]*cnt[col[L]]*cnt[col[L]];
cnt[col[L]] --;
temp += cnt[col[L]]*cnt[col[L]]*cnt[col[L]];
L ++;
}
while(L > cmd[i].l){
L --;
temp -= cnt[col[L]]*cnt[col[L]]*cnt[col[L]];
cnt[col[L]] ++;
temp += cnt[col[L]]*cnt[col[L]]*cnt[col[L]];
}
ans[cmd[i].id] = temp;
}
}
int main()
{
//freopen("test.in","r",stdin);
while(~scanf("%d",&n)){
FOR(i,0,n) {scanf("%I64d",&con[i].note);con[i].id = i+1;}
sort(con,con+n);
int cot = 0;
col[con[0].id] = (++cot);
FOR(i,1,n){
if(con[i].note != con[i-1].note) col[con[i].id] = (++cot);
else col[con[i].id] = cot;
}
scanf("%d",&m);
FOR(i,0,m){
scanf("%d%d",&cmd[i].l,&cmd[i].r);
cmd[i].id = i;
}
sort(cmd,cmd+m);
solve();
FOR(i,0,m){
printf("%I64d\n",ans[i]);
}
}
return 0;
}
3:Group http://acm.hdu.edu.cn/showproblem.php?pid=4638 维护一个左边数的位置,右边数的位置,1,n特判一下就行了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
#define N 100010
#define FOR(i,x,y) for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
using namespace std;
const int M = (int)sqrt(1.0*N);
int num[N],pos[N],ans[N],l[N],r[N];
int n,m;
struct Commend{
int l,r;
int id;
bool operator < (const Commend& rhs) const{
if(l/M == rhs.l/M) return r < rhs.r;
return (l/M < rhs.l/M);
}
}cmd[N];
void solve(){
int L = 1,R = 0;
int temp = 0;
FOR(i,0,m){
while(R < cmd[i].r){
R ++;
if(l[num[R]] >= L && r[num[R]] <= R) temp --;
else if((l[num[R]] < L && r[num[R]] > R) || (l[num[R]] > R) || (r[num[R]] < L)) temp ++;
}
while(R > cmd[i].r){
if(l[num[R]] >= L && r[num[R]] <= R) temp ++;
else if((l[num[R]] < L && r[num[R]] > R) || (l[num[R]] > R) || (r[num[R]] < L)) temp --;
R --;
}
while(L < cmd[i].l){
if(l[num[L]] >= L && r[num[L]] <= R) temp ++;
else if((l[num[L]] < L && r[num[L]] > R) || (l[num[L]] > R) || (r[num[L]] < L)) temp --;
L ++;
}
while(L > cmd[i].l){
L --;
if(l[num[L]] >= L && r[num[L]] <= R) temp --;
else if((l[num[L]] < L && r[num[L]] > R) || (l[num[L]] > R) || (r[num[L]] < L)) temp ++;
}
ans[cmd[i].id] = temp;
}
}
int main()
{
//freopen("test.in","r",stdin);
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
FOR(i,1,n+1){
scanf("%d",&num[i]);
pos[num[i]] = i;
}
l[1] = 0;
r[1] = pos[2];
r[n] = n+1;
l[n] = pos[n-1];
FOR(i,2,n){
l[i] = min(pos[i-1],pos[i+1]);
r[i] = max(pos[i-1],pos[i+1]);
}
FOR(i,0,m){
scanf("%d%d",&cmd[i].l,&cmd[i].r);
cmd[i].id = i;
}
sort(cmd,cmd+n);
solve();
FOR(i,0,m){
printf("%d\n",ans[i]);
}
}
return 0;
}
4:Zero Query http://www.spoj.com/problems/ZQUERY/en/ 我wa到死的一题。这个题要维护一个左边最靠近的一个位置,右边靠近的一个位置,就行了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#define FOR(i,x,y) for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define lrt rt<<1
#define rrt rt<<1|1
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define ll long long
#define N 50050
using namespace std;
int M;
int cnt,col[N],sum[N],n,m,l[N],r[N],st,pr[N],pl[N],ans[N],pos[N];
int maxn,minn;
struct Commend{
int l,r;
int id;
bool operator < (const Commend& rhs) const{
if(l/M == rhs.l/M) return r < rhs.r;
return (l/M) < (rhs.l/M);
}
}cmd[N];
struct Tree{
int l,r;
int maxx;
}tree[N<<2];
void Build(int rt,int l,int r){
tree[rt].l = l; tree[rt].r = r; tree[rt].maxx = 0;
if(l == r) return;
int mid = (l+r)>>1;
Build(lson);
Build(rson);
}
void PushUp(int rt) {tree[rt].maxx = max(tree[lrt].maxx,tree[rrt].maxx);}
void Modify(int rt,int k,int val){
if(tree[rt].l == tree[rt].r) {tree[rt].maxx = val;return;}
int mid = (tree[rt].l + tree[rt].r)>>1;
if(k <= mid) Modify(lrt,k,val);
else Modify(rrt,k,val);
PushUp(rt);
}
void solve(){
int L = 1,R = 0;
memset(r,-1,sizeof(r));
memset(l,-1,sizeof(l));
FOR(i,0,m){
while(R < cmd[i].r){
R ++;
if(l[col[R]] == -1) l[col[R]] = R;
r[col[R]] = R;
Modify(1,col[R],r[col[R]]-l[col[R]]);
}
while(R > cmd[i].r){
r[col[R]] = pr[R];
if(r[col[R]] < L) l[col[R]] = r[col[R]] = -1;
Modify(1,col[R],r[col[R]]-l[col[R]]);
R --;
}
while(L < cmd[i].l){
l[col[L]] = pl[L];
if(l[col[L]] > R || l[col[L]] == -1) l[col[L]] = r[col[L]] = -1;
Modify(1,col[L],r[col[L]]-l[col[L]]);
L ++;
}
while(L > cmd[i].l){
L --;
if(r[col[L]] == -1) r[col[L]] = L;
l[col[L]] = L;
Modify(1,col[L],r[col[L]]-l[col[L]]);
}
ans[cmd[i].id] = tree[1].maxx;
}
}
int main()
{
//freopen("test.in","r",stdin);
while(~scanf("%d%d",&n,&m)){
M = (int)sqrt(1.0*n);
int tem;
sum[1] = col[1] = 0;
maxn = minn = 0;
FOR(i,2,n+2){
scanf("%d",&tem);
sum[i] = sum[i-1] + tem;
if(tem == 1) maxn = max(maxn,sum[i]);
else minn = min(minn,sum[i]);
}
cnt = 1-minn;
FOR(i,1,n+2){
col[i] = sum[i] + cnt;
}
cnt = maxn-minn+1;
Build(1,1,cnt);
FOR(i,0,m){
scanf("%d%d",&cmd[i].l,&cmd[i].r);
cmd[i].r ++;
cmd[i].id = i;
}
sort(cmd,cmd+m);
FOR(i,1,cnt+1) pos[i] = -1;
FOR(i,1,n+2){
pr[i] = pos[col[i]];
pos[col[i]] = i;
}
FOR(i,1,cnt+1) pos[i] = -1;
IFOR(i,n+1,0){
pl[i] = pos[col[i]];
pos[col[i]] = i;
}
solve();
FOR(i,0,m){
printf("%d\n",ans[i]);
}
}
return 0;
}
5:The sum of gcd http://acm.hdu.edu.cn/showproblem.php?pid=5381 和上一题有点像,稍微复杂一点,可以看我这道题写的题解
http://blog.csdn.net/u014610830/article/details/47670615