ceoi 2011 treasure hunt

21 篇文章 0 订阅
1 篇文章 0 订阅

wc上第一次听这个题就感觉很有想法,于是想了个倍增,只能过50分,后来周而进讲了分块存图的方法,可惜只能用动态树维护,经ATM启发,每次分块后都不裂块,以适应倍增的树形态不变的性质,也就是后来钟沛林讲的方法,实现起来还比较容易,只是细节难以处理。

每个块我只存块状树深度,左右端点,每要处理一个点,二分其所处的块,倍增存2^k步的块,2^k步的点,2^k到块顶的距离,然后各种特判维护实际距离。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
struct block
{
    int d,l,r;
}st[500000];
int ss,num,f[500000][30],g[500000][30],c[500000][30],a2[30],dis1,dis2,ans;
void init()
{
    int i,k;
    st[ss=1].d=0,st[1].l=1,st[1].r=1,num=1;
    for (i=1,k=29,a2[0]=1;i<=k;a2[i]=a2[i-1]*2,i++) ;
}
int search(int x)
{
    int l=1,r=ss,mid;
    for (;l<=r;)
    {
        mid=(l+r)>>1;
        if (st[mid].r<x) l=mid+1;else r=mid-1;
    }
    return l;
}
void path(int a,int b)
{
    int aa,bb,k,i;
    aa=search(a),st[++ss].d=st[aa].d+1,st[ss].l=num+1,st[ss].r=num+b,num+=b;
    f[ss][0]=aa;g[ss][0]=a,c[ss][0]=a-st[aa].l+1;
    k=(int)log2(st[ss].d);
    for (i=1;i<=k;i++)
        if (a2[i]<=st[ss].d)
            g[ss][i]=g[f[ss][i-1]][i-1],
            c[ss][i]=c[ss][i-1]+c[f[ss][i-1]][i-1],
            f[ss][i]=f[f[ss][i-1]][i-1];
}
int make(int a,int d)
{
    int e,ia,k,aa;
    e=d,aa=search(a),ia=a;
    if (a-d>=st[aa].l) return (a-d);
    e-=a-st[aa].l,ia=st[aa].l;
    for (k=0;e>0;)
    {
        if ((e-c[aa][k]>=0)&&(c[aa][k]!=0)) e-=c[aa][k],ia=g[aa][k],aa=f[aa][k],k++;
        else {
                if (0==k) e-=c[aa][k],ia=g[aa][k],aa=f[aa][k],k++;
                else k--;
            }
    }
    e+=ia-st[aa].l;return ia-e;
}
int min(int a,int b) {return (a<b) ? a : b;}
int dig(int a,int b)
{
    int ya,yb,e,aa,bb,ia,ib,pd,k,oa,ob;
    aa=search(a),bb=search(b),pd=0,oa=a,ob=b;
    if (aa==bb) {k=(a+b)/2;if ((a>b)&&((a+b)%2==1)) k++;return k;}
    if (st[aa].d<st[bb].d) e=aa,aa=bb,bb=e,e=a,a=b,b=e,pd=1;
    ya=aa,yb=bb;
    e=st[aa].d-st[bb].d,dis1=0,dis2=0,ia=st[aa].l,ib=st[bb].l,k=0;
    for (;e;k++,e>>=1)
        if ((e&1)==1) dis1+=c[aa][k],ia=g[aa][k],aa=f[aa][k];    
    for (k=0;aa!=bb;)
    {
        if (f[aa][k]!=f[bb][k]) dis1+=c[aa][k],ia=g[aa][k],aa=f[aa][k],dis2+=c[bb][k],ib=g[bb][k],bb=f[bb][k],k++;
        else {
                    if (0==k) dis1+=c[aa][k],ia=g[aa][k],aa=f[aa][k],dis2+=c[bb][k],ib=g[bb][k],bb=f[bb][k],k++;
                    else k--;
            }
    }
    int flag=dis2;
    if (flag!=0)
        if (ia<ib) dis2+=ib-ia;else dis1+=ia-ib;
    else if (b>ia) dis2+=b-ia;else dis1+=ia-b;
    if (dis1!=0) dis1-=(ia-st[aa].l);dis1+=a-st[ya].l;
    if (flag!=0) dis2-=(ib-st[aa].l),dis2+=b-st[yb].l;//else dis2=b-ia;
    if (0==flag) ib=b;    
    if (pd) e=ia,ia=ib,ib=e,e=dis1,dis1=dis2,dis2=e;    
    if (dis1==dis2) return min(ia,ib);
    if (dis1>dis2) return make(oa,(dis1+dis2)/2);
    else 
    {
        k=(dis1+dis2)/2;
        if (((dis1+dis2)&1)==1) k++;
        return make(ob,k);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值