昨天问别人莫队算法, 然后今天才知道他说的那个好像根本就不叫莫队算法, 只是因为分块排序之后扫一遍的方法也是n * sqrt(n), 很好写但是可以用来做所有可以用莫队做的题吧。。。。
比如说这道 莫队出的小z 的袜子,,这样写就40行诶。。建一个那种的肯定又要上百了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define MAXN 50005
using namespace std;
struct ask{
int l, r, pos;long long zi, mu;
}d[MAXN], ans[MAXN];
int n, m, a[MAXN], kuai, num[MAXN];
int cmp(ask a, ask b){
if((a.l / kuai == (b.l / kuai)))return a.r < b.r;
return (a.l / kuai) < (b.l / kuai);
}
long long gcd(long long a, long long b){
if(!a)return b; return gcd(b % a, a);
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++)scanf("%d", &a[i]);
for(int i = 1; i <= m; i ++){scanf("%d%d", &d[i].l, &d[i].r);d[i].pos = i; ans[i].mu = (long long)(d[i].r - d[i].l + 1) * (d[i].r - d[i].l) / 2;}
kuai = sqrt(m);
sort(d + 1, d + m + 1, cmp);
int l = 1, r = 0; long long tmp = 0;
for(int i = 1; i <= m; i ++){
while(r < d[i].r){r ++; tmp += num[a[r]]; num[a[r]] ++;}
while(r > d[i].r){num[a[r]] --; tmp -= num[a[r]]; r --;}
while(l < d[i].l){num[a[l]] --; tmp -= num[a[l]]; l ++;}
while(l > d[i].l){l --; tmp += num[a[l]]; num[a[l]] ++;}
ans[d[i].pos].zi = tmp;
}
for(int i = 1; i <= m; i ++){
long long gg = gcd(ans[i].zi, ans[i].mu);
printf("%lld/%lld\n", ans[i].zi / gg, ans[i].mu / gg);
}
return 0;
}
还有poj 3241 的这道题,
为了练习我 “借鉴” 别人敲了一个莫队, 然后就调不出来了。。。
没错 借鉴 是因为我了解地不够, 以后一定要再深入地了解它。
先搬一个别人的代码吧。。。和下面这个是一个人的 莫队算法
/*
* Author: vawait
* Created Time: 2014/3/4 16:08:29
* File Name: test.cpp
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<stack>
#include<ctime>
using namespace std;
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define red(i, a, b) for (int i = (a); i >= (b); --i)
#define clr( x , y ) memset(x,y,sizeof(x))
#define mp make_pair
#define pb push_back
#define sqr(x) ((x) * (x))
typedef long long lint;
const int maxn = 10010;
int n , m , t , x , f[maxn] , c[maxn];
struct bit
{
int t[10100] , n;
void cl() { clr( t, 0 ); }
void add(int x,int y) {
for ( ; x; x -= x & (-x) ) if( c[y] < c[t[x]] ) t[x] = y;
}
int ask(int x) {
int s = 0;
for ( ; x <= 10000; x += x & (-x) ) if ( c[s] > c[t[x]] ) s = t[x];
return s;
}
} T;
struct nodd
{
int x , y , n;
} a[maxn] , d[maxn] , b[4*maxn];
bool cmp(nodd a,nodd b) { return a.x == b.x ? a.y < b.y : a.x < b.x; }
bool cmp2(nodd a,nodd b) { return a.n < b.n; }
void add(int x,int y,int d)
{
b[++t].x = x; b[t].y = y;
b[t].n = d;
}
void init()
{
rep(i,1,n) scanf("%d%d",&a[i].x,&a[i].y) , a[i].n = i;
t = 0;
rep(k,1,4) {
if ( k == 2 || k == 4 ) rep(i,1,n) swap(a[i].x,a[i].y);
if ( k == 3 ) rep(i,1,n) a[i].x = -a[i].x;
sort( a + 1 , a + 1 + n , cmp );
rep(i,1,n) d[i].x = a[i].y - a[i].x , d[i].y = i;
rep(i,1,n) c[i] = a[i].x + a[i].y;
c[0] = 2000000000;
sort( d + 1 , d + 1 + n , cmp );
rep(i,1,n) f[d[i].y] = i;
T.cl();
red(i,n,1) {
if ( x = T.ask(f[i]) ) add( a[i].n , a[x].n , abs( a[i].x - a[x].x ) + abs( a[i].y - a[x].y ) );
T.add(f[i],i);
}
}
}
int find(int t) { return f[t] == t ? t : f[t] = find(f[t]); }
void work()
{
int s = n;
rep(i,1,n) f[i] = i;
sort( b + 1 , b + 1 + t , cmp2 );
rep(i,1,t) if ( find(b[i].x) != find(b[i].y) ) {
f[find(b[i].x)] = find(b[i].y);
s --;
if ( s == m ) cout << b[i].n << endl;
}
}
int main()
{
while ( cin >> n >> m ) {
init();
work();
}
return 0;
}
//下面是我认为介绍的比较易懂的一篇
可以证明每一个顶点在最小生成树中在这45度的范围内至多仅有一条边与之相连,也就是如果以一个点为原点,那么上面分成的八个区域里面最多和每个区域连一条边。
首先只考虑R1区间的。把每个坐标都按x为第一关键字,y为第二关键字排序。从后往前处理,那么能保证处理到第i个点时,比Xi大的点都已经处理过了。也就是以第i点为原点时,其他点只在R1,、R2、R3、R4区间。现在找R1区间的点,通过位移使得i点在原点,那么R1区间的点必然满足Xj<Yj,因为i点为原点,所以在原坐标中满足Xj-Xi>Yj-Yi,转移一下得Yj-Xj<Yi-Xi。也就是在Xj>Xi并且满足Yj-Xj<Yi-Xi中找一个离i点最近的点。因为Xi<Xj,Yi<Yj,所以曼哈顿距离为(Xj+Yj)-(Xi+Yi),后面的为常数,只考虑前面的即可。
总的算法是先排序,按x为第一关键字,y为第二关键字。然后以每个点的Yi-Xi的值得出排名F[i]。按排序的顺序倒序处理i点,每次找F[j]比F[i]大的并且是Xj+Yj最小的点连边。这个可以以F[i]作为标号,Xi+Yi作为值,用线段树或者树状数组维护即可。
考虑到对称性所以只需要处理4个区域,每个区域可以通过对称转移到R1,所以循环四次每次只是处理坐标,算法还是一样。
这就是莫队算法了,如果知道区间[l,r]的信息,可以O(1)得到[l+1,r]的信息,那么其转移复杂度就相当于曼哈顿距离。可以找出曼哈顿最小生成树然后按照连边处理信息。
最后是我的代码,只有莫队算法处理边还没有求最小生成树的: