转载请注明出处,谢谢http://blog.youkuaiyun.com/ACM_cxlove?viewmode=contents by---cxlove
可怕的莫队算法。。。感觉有了这个,是不是可以解决所有的区间查询但是无修改的题目
也许不是最优的,但是O(n*sqrt(n))完全也是可以尝试的。
莫队算法:对于两个区间的查询[l1,r1] ,[l2,r2]如果每增加一个区间元素或者删除,都能做到O(1)的话
那么从[l1,r1]转移到[l2,r2],暴力可以做到|l1-l2|+|r1-r2|,就是manhattan距离
莫队的论文一直没有找到,所以只是大致的了解下,应该是证明出构造出哈密尔顿路径是最优的。
但是可以用manhattan mst来构造,大约会是两倍,然后莫队证明出这样转移的上限是O(n*sqrt(n))。
所以对于这种无修改的区间查询来说
可以先将所有的区间,看成二维平面上的点,求一次manhattan mst,然后根据mst来进行转移
相邻的两个区间的查询转移,暴力解决。
Manhattan MST 这里有
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#define lowbit(x) (x&(-x))
#define LL long long
using namespace std;
const int N = 50005;
struct Point{
int x,y,id;
bool operator<(const Point p)const{
return x!=p.x?x<p.x:y<p.y;
}
}p[N],pp[N];
//数状数组,找(y-x)大于当前的,但是y+x最小的
struct BIT{
int min_val,pos;
void init(){
min_val=(1<<30);
pos=-1;
}
}bit[N];
//所有有效边,Kruskal
struct Edge{
int u,v,d;
bool operator<(const Edge e)const{
return d<e.d;
}
}e[N<<2];
//前向星
struct Graph{
int v,next;
}edge[N<<1];
int n,m,tot,pre[N];
int total,start[N];
int find(int x){
return pre[x]=(x==pre[x]?x:find(pre[x]));
}
inline int dist(int i,int j){
return abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y);
}
inline void addedge(int u,int v,int d){
e[tot].u=u;
e[tot].v=v;
e[tot++].d=d;
}
inline void _add(int u,int v){
edge[total].v=v;
edge[total].next=start[u];
start[u]=total++;
}
inline void update(int x,int val,int pos){
for(int i=x;i>=1;i-=lowbit(i))
if(val<bit[i].min_val)
bit[i].min_val=val,bit[i].pos=pos;
}
inline int ask(int x,int m){
int min_val=(1<<30),pos=-1;
for(int i=x;i<=m;i+=lowbit(i))
if(bit[i].min_val<min_val)
min_val=bit[i].min_val,pos=bit[i].pos;
return pos;
}
inline void Manhattan_minimum_spanning_tree(int n,Point *p){
int a[N],b[N];
for(int dir=0;dir<4;dir++){
//4种坐标变换
if(dir==1||dir==3){
for(int i=0;i<n;i++)
swap(p[i].x,p[i].y);
}
else if(dir==2){
for(int i=0;i<n;i++){
p[i].x=-p[i].x;
}
}
sort(p,p+n);
for(int i=0;i<n;i++){
a[i]=b[i]=p[i].y-p[i].x;
}
sort(b,b+n);
int m=unique(b,b+n)-b;
for(int i=1;i<=m;i++)
bit[i].init();
for(int i=n-1;i>=0;i--){
int pos=lower_bound(b,b+m,a[i])-b+1; //BIT中从1开始
int ans=ask(pos,m);
if(ans!=-1)
addedge(p[i].id,p[ans].id,dist(i,ans));
update(pos,p[i].x+p[i].y,i);
}
}
sort(e,e+tot);
for(int i=0;i<n;i++)
pre[i]=i;
for(int i=0;i<tot;i++){
int u=e[i].u,v=e[i].v;
int fa=find(u),fb=find(v);
if(fa!=fb){
pre[fa]=fb;
_add(u,v);
_add(v,u);
}
}
}
LL gcd(LL a,LL b){
return b==0?a:gcd(b,a%b);
}
LL up[N],down[N];
LL ans;
int col[N],vis[N]={0};
int cnt[N]={0}; //记录每种颜色出现的次数
inline void add(int l,int r){
for(int i=l;i<=r;i++){
int c=col[i];
ans-=(LL)cnt[c]*(cnt[c]-1)/2;
cnt[c]++;
ans+=(LL)cnt[c]*(cnt[c]-1)/2;
}
}
inline void del(int l,int r){
for(int i=l;i<=r;i++){
int c=col[i];
ans-=(LL)cnt[c]*(cnt[c]-1)/2;
cnt[c]--;
ans+=(LL)cnt[c]*(cnt[c]-1)/2;
}
}
//[l1,r1]前一个区间 [l2,r2]当前区间
void dfs(int l1,int r1,int l2,int r2,int idx,int pre){
if(l2<l1) add(l2,l1-1);
if(r2>r1) add(r1+1,r2);
if(l2>l1) del(l1,l2-1);
if(r2<r1) del(r2+1,r1);
up[pp[idx].id]=ans;
vis[idx]=1;
for(int i=start[idx];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(vis[v]) continue;
dfs(l2,r2,pp[v].x,pp[v].y,v,idx);
}
if(l2<l1) del(l2,l1-1);
if(r2>r1) del(r1+1,r2);
if(l2>l1) add(l1,l2-1);
if(r2<r1) add(r2+1,r1);
}
int main(){
//freopen("input.txt","r",stdin);
scanf("%d%d",&n,&m);
tot=total=0;
memset(start,-1,sizeof(start));
for(int i=1;i<=n;i++)
scanf("%d",&col[i]);
for(int i=0;i<m;i++){
scanf("%d%d",&p[i].x,&p[i].y);
down[i]=(LL)(p[i].y-p[i].x+1)*(p[i].y-p[i].x)/2;
p[i].id=i;
pp[i]=p[i]; //副本一份,便于后面DFS,或者之后按id排序
}
Manhattan_minimum_spanning_tree(m,p);
for(int i=0;i<m;i++){
p[i].y=-p[i].y;
}
dfs(2,1,pp[0].x,pp[0].y,0,-1);
for(int i=0;i<m;i++){
LL g=gcd(up[i],down[i]);
printf("%lld/%lld\n",up[i]/g,down[i]/g);
}
return 0;
}