本以为莫队算法是很简单的东西、、、
但是学完了吓得我筷子都掉地上了、、、
先来看看我的提交记录、、、
再看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)),简单证明一下大概是这个意思:
假设我们这两个点在同一块,显然移动询问区间的代价<sqrt(n),
考虑这两个点相差仅仅一块,移动询问区间的代价大概是2 * 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<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#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,现在还不知道错误到底在哪、、、按说上面和下面的语句都是等效的、、、
等一会再调一下、、、