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

转载请注明出处,谢谢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];  
//所有有效边,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;  
}  


没有更多推荐了,返回首页