[2009国家集训队]小Z的袜子(hose)

来源:互联网 发布:java应用服务器开发 编辑:程序博客网 时间:2024/04/29 11:05

转载请注明出处,谢谢http://blog.csdn.net/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];  //所有有效边,Kruskalstruct 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;  }