【莫队算法】小z的袜子

来源:互联网 发布:linux查看cpu命令 编辑:程序博客网 时间:2024/04/26 07:09
题目:给定n个数a1, a2…… anm个询问(L,R)。对于每个询问,从aL, aL+1…… aRR-L+1个数中随机取出两个数,求这两个数相同的概率。

假设我们当前处理了询问(l1,r1),那么下个询问(l2,r2)需要操作的次数是|l1-l2|+|r1-r2|,其实就是曼哈顿距离,那么只需求出哈密尔顿路径即可确定操作序列,但是tsp不好求,则我们求出曼哈顿距离最小生成树,可知大小不超过tsp的两倍,莫队证出按此顺序操作复杂度是o(nsqrt(m))

曼哈顿距离最小生成树据说可以分成两块做,但是我实在觉得偏序关系处理起来有问题,所以还是扫了4遍,因为不想离散,所以用了一个奇怪的顺序排的


#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>const int oo=1073741819;int tail[50005],next[1000000],sora[1000000],a[50005],c[50005],l[50005],r[50005],x[50005],y[50005],b[5][131072];int ll[500000],rr[500000],w[500000],f[50005],u[500000];int tim,n,m,m1,ss,tot,p,q,low,lim;long long ans,ans1[50005],ans2[50005],tmp;bool v[50005];void origin() {    for (m1=1;m1<=n+2;m1<<=1) ;m1++;    for (int i=1;i<=m+1;i++) tail[i]=i;ss=m+1,tot=0;}inline int cmp1(const void *i,const void *j){    p=*(int *)i,q=*(int *)j;    if (y[p]-x[p]!=y[q]-x[q]) return (y[q]-x[q])-(y[p]-x[p]);    return x[p]-x[q];}inline int cmp2(const void *i,const void *j){    p=*(int *)i,q=*(int *)j;    if (y[p]+x[p]!=y[q]+x[q]) return (y[q]+x[q])-(y[p]+x[p]);    return x[p]-x[q];}inline int min(int e,int a,int b){    if (e<=2) return (x[a]+y[a]<=x[b]+y[b]) ? a : b;    return (y[a]-x[a]<=y[b]-x[b]) ? a : b;}inline void change(int e,int x,int u){    for (x+=m1;x;x>>=1) {b[e][x]=min(e,b[e][x],u);if (b[e][x]!=u) return ;    }}inline int ask(int e,int l,int r){    int sum=0;    if (l>r) return 0;    l+=m1-1,r+=m1+1;    for (;!(1==(l^r));l>>=1,r>>=1) {if (0==(l&1)) sum=min(e,sum,b[e][l+1]);if (1==(r&1)) sum=min(e,sum,b[e][r-1]);    }    return sum;}void link(int p,int q){    if (!p || !q) return ;    tot++;    ll[tot]=p,rr[tot]=q,w[tot]=abs(x[p]-x[q])+abs(y[p]-y[q]);}void mysoul(){    int i,xx;    x[0]=0,y[0]=oo;    qsort(u+1,m+1,sizeof(u[1]),cmp1);tim=1;    for (i=1;i<=m+1;i++) {xx=ask(tim,x[u[i]],n);link(u[i],xx);change(tim,x[u[i]],u[i]);    }    tim=2;    for (i=m+1;i>=1;i--) {xx=ask(tim,y[u[i]]+1,n);link(u[i],xx);change(tim,y[u[i]],u[i]);    }    qsort(u+1,m+1,sizeof(u[1]),cmp2);tim=3;    for (i=1;i<=m+1;i++) {xx=ask(tim,0,x[u[i]]-1);link(u[i],xx);change(tim,x[u[i]],u[i]);    }    tim=4;    for (i=m+1;i>=1;i--) {xx=ask(tim,y[u[i]],n);link(u[i],xx);change(tim,y[u[i]],u[i]);    }}int find(int x) {if (f[x]!=x) f[x]=find(f[x]);return f[x];}int cmp3(const void *i,const void *j) {return w[*(int *)i]-w[*(int *)j];}long long gcd(long long a,long long b){    long long t;    for (;a%b;) {t=a%b;a=b;b=t;    }    return b;}inline void del(int l,int r) {for (int i=l;i<=r;i++) c[a[i]]--,ans-=c[a[i]];}inline void add(int l,int r) {for (int i=l;i<=r;i++) ans+=c[a[i]],c[a[i]]++;}void dfs(int x){    int i,ne;    if (r[x]>lim) add(lim+1,r[x]);    if (l[x]<low) add(l[x],low-1);    if (r[x]<lim) del(r[x]+1,lim);    if (l[x]>low) del(low,l[x]-1);    low=l[x],lim=r[x];    ans1[x]=ans,v[x]=1;    for (i=x;next[i]!=0;) {i=next[i],ne=sora[i];if (!v[ne]) dfs(ne);    }}void link2(int x,int y){    ss++,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y;    ss++,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x;}void init(){    int i,ls,rs;    scanf("%d%d\n",&n,&m);    origin();    for (i=1;i<=n;i++) scanf("%d",&a[i]);    for (i=1;i<=m;i++) {scanf("%d%d\n",&l[i],&r[i]);x[i]=l[i],y[i]=r[i]+1;ans2[i]=((long long)r[i]-l[i]+1)*(r[i]-l[i])/2;u[i]=i;    }    l[m+1]=r[m+1]=x[m+1]=y[m+1]=0,u[m+1]=m+1;    mysoul();    for (i=1;i<=tot;i++) u[i]=i;    qsort(u+1,tot,sizeof(u[1]),cmp3);    for (i=1;i<=m+1;i++) f[i]=i;    for (i=1,tmp=m;i<=tot && tmp;i++) {ls=find(ll[u[i]]),rs=find(rr[u[i]]);if (ls!=rs) f[ls]=rs,tmp--,link2(ll[u[i]],rr[u[i]]);    }    low=lim=1,ans=0,c[a[1]]=1;    dfs(m+1);    for (i=1;i<=m;i++) {tmp=gcd(ans1[i],ans2[i]);printf("%I64d/%I64d\n",ans1[i]/tmp,ans2[i]/tmp);    }}int main(){    freopen("hose.in","r",stdin);    freopen("hose.out","w",stdout);     init();    return 0;}


原创粉丝点击