学习莫队算法、、、

本以为莫队算法是很简单的东西、、、
但是学完了吓得我筷子都掉地上了、、、
先来看看我的提交记录、、、
再看BZOJ:
其实错了这么多次确实不应该交了、、、
但是、、、没想到的是、、、
我的模板都是错的、、、
先来讲讲莫队算法、、、
**离线**算法,不带修改,多次询问,[l,r]可以O(1)求出[l,r + 1],[l,r - 1],[l - 1,r],[l + 1,r]。
那么我们可以用莫队算法来做、、、
大概是这样的:
我们把每次询问:[l,r]看成是一个二维平面的点a[l,r],所以所有询问就能看成:
二维平面的一群散点、、、
现在我们从一个点出发,花费最小的代价使得每个散点都到达、、
而代价就是每个点之间的manhattan距离、、、
即:
求二维平面的点的manhattan距离最小生成树、、、
均摊复杂度是O(nsqrt(n)),但是编程复杂度过高,不推(hui)荐(xie)。


----------
另一种想法是分块思想:
将原来的**序列**分成sqrt(n)块,那么我们只要对**不在一块中的**询问按照左端点排序,**在同一块中的**询问按照右侧端点排序即可、、、

均摊复杂度是O(nsqrt(n)),简单证明一下大概是这个意思:
假设我们这两个点在同一块,显然移动询问区间的代价
现在来讲讲我写莫队挂掉两次的故事:
WA && TLE:
先来讲讲我是怎么TLE的……
请注意我之前分块思想的一句话:
将原来的**序列**分成sqrt(n)块。
将原来的**序列**分成sqrt(n)块。
将原来的**序列**分成sqrt(n)块。
是啊怎么了?
然后我傻乎乎地写了:
int BLOCK = sqrt(n);
Q[i].Block = i / BLOCK + 1;
实际上:
Q[i].Block = Q[i].l/ BLOCK + 1;
所以说不要写混……
区别大概就是:
一个TLE一个AC的区别、、、
然后继续讲一下迷之WA:

#include
#include
#include
#include
#define Rep(i,n) for(int i = 1; i <= n ; i ++)
#define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next)
#define Rep_d(i,n) for(int i = n ; i > 0 ; i --)
#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)
#define RD(i,x,n) for(int i = x; i <= n ; i ++)
#define CLR(a,b) memset(a,b,sizeof(a))
#define fl edge[i].f
#define vfl edge[i^1].f
#define v edge[i].to
#define N 50005
using namespace std;
const int inf = 1 << 30;
typedef long long ll;
int n,m,seq[N],BLK[N];
int read(){
	char ch = getchar();
	while(ch < '0' || ch > '9')ch = getchar ();
	int x = 0;
	while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar ();
	return x;
}
ll num[N];
ll gcd(int a,int b){return b ? gcd(b,a % b) : a;}
struct QRY{
	int l,r,idx;
}Q[50005];
ll ans = 0;
ll Res[N][2];
void Upd(int x,int d){
	ans -= num[seq[x]] * num[seq[x]];
	num[seq[x]] += d;
	ans += num[seq[x]] * num[seq[x]];
}
bool cmp(QRY A,QRY B){
	if(BLK[A.l] == 	BLK[B.l])
        return A.r < B.r;
    return A.l < B.l;
}
int main()
{
	while(~scanf("%d%d",&n,&m)){
		CLR(num,0);
		int BLOCK = sqrt(n);
		Rep(i,n){
			seq[i] = read();
			BLK[i] = (i - 1) / BLOCK + 1;
		}
		Rep(i,m) 
			Q[i].l = read(),Q[i].r = read(),Q[i].idx = i;
		sort(Q + 1,Q + 1 + m,cmp);
		int l = 1,r = 0;
		Rep(i,m)
		{
			if(Q[i].l == Q[i].r){
				Res[Q[i].idx][0] = 0;
				Res[Q[i].idx][1] = 1;
				continue;
			}
			
			/*if(qu[i].l==qu[i].r)
            {
                up[id]=0,dw[id]=1;
                continue;
            }
            if(r < Q[i].r)
            {
                for(int j = r + 1;j <= Q[i].r;j ++) //R + 1 - > QR ++
                    Upd(j,1);
            }
            else
            {
                for(int j = r;j > Q[i].r;j --)
                    Upd(j,-1);
            }
            r = Q[i].r;
            if(l < Q[i].l)
            {
                for(int j = l;j < Q[i].l;j ++)
                    Upd(j,-1);
            }
            else
            {
                for(int j = l - 1;j >= Q[i].l;j --) 
                    Upd(j,1);
            }
            l = Q[i].l;
			*/
			while(l > Q[i].l)Upd(-- l,1);//printf("EXTEND L:%d\n",l);
			while(r < Q[i].r)Upd(++ r,1);//,printf("EXTEND R:%d\n",r);
			while(l < Q[i].l)Upd(l ++,-1);//,printf("DISEXTEND L:%d\n",l - 1);
			while(r > Q[i].r)Upd(r --,-1);//,printf("DISEXTEND R:%d\n",r + 1);
			//printf("R:%d QR:%d\n",r,Q[i].r);
			//printf("L : %d QL : %d\n",l,Q[i].l);
			ll UP = ans - Q[i].r + Q[i].l - 1;
			ll DOWN = (ll)(Q[i].r - Q[i].l + 1) * (Q[i].r - Q[i].l);
			ll dd = gcd(UP,DOWN);
			Res[Q[i].idx][0] = UP/dd;
			Res[Q[i].idx][1] = DOWN/dd;
			//puts("THIS QUERY IS END.");
		}
		Rep(i,m)
			printf("%lld/%lld\n",Res[i][0],Res[i][1]);	
	}
	return 0;
}

这个无论是I64d还是lld,无论是BZOJ还是清橙、、、都是WA,现在还不知道错误到底在哪、、、按说上面和下面的语句都是等效的、、、
等一会再调一下、、、


你可能感兴趣的:(数据结构)