zoukankan      html  css  js  c++  java
  • 洛谷1081 (NOIp2012) 开车旅行——倍增预处理

    题目:https://www.luogu.org/problemnew/show/P1081

    预处理从每个点开始a能走多少、b能走多少。可以像dp一样从后往前推。

    但有X的限制。所以该数组可以变成倍增的样子。

    预处理第一步的找下一个点可以从后往前弄,在树状数组上二分。然后正常转移即可。

    树状数组上的二分有细节:

      找第一个 f 值为需要的值k的,可以if( query( mid )>=k )ans=mid,r=mid-1;也可以if( query(mid)<=k-1 )ans=mid,l=mid+1; ans++;

    倍增数组也有细节,大约就是当目的地为0的时候就不要给距离赋值了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define ll long long
    using namespace std;
    const int N=1e5+5;
    const double eps=1e-10,INF=2e9+5;
    int n,m,h[N],tp[N],f[N],ps[N],S;
    ll X,dp[N][20][3],mp[N],s[N],ca,cb;
    double tc=INF;
    int rdn()
    {
        int ret=0,fx=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')fx=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')(ret*=10)+=ch-'0',ch=getchar();
        return ret*fx;
    }
    ll rdl()
    {
        ll ret=0,fx=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')fx=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')(ret*=10)+=ch-'0',ch=getchar();
        return ret*fx;
    }
    void add(int x,int k){for(;x<=m;x+=(x&-x))f[x]+=k;}
    int query(int x){int ret=0;for(;x;x-=(x&-x))ret+=f[x];return ret;}
    void find(int i,int &ps0,int &ps1)
    {
        ps0=0;ps1=0;
        int k=query(h[i]),l=1,r=m,tp0=0,tp1=0,antp=0;
    //    printf("i=%d k=%d
    ",i,k);
        if(n-i+1>k)
        {
            while(l<=r)
            {
                int mid=l+r>>1;
                if(query(mid)<=k)tp0=mid,l=mid+1;
                else r=mid-1;
            }
            tp0++;
        }
        if(k>1)
        {
            l=1;r=m;
            while(l<=r)
            {
                int mid=l+r>>1;
                if(query(mid)>=k-1)tp1=mid,r=mid-1;
                else l=mid+1;
            }
        }
        if(!tp0&&tp1)ps0=ps[tp1],antp=tp1;else if(tp0&&!tp1)ps0=ps[tp0],antp=tp0;
        else if(tp0&&tp1)
            {if(tp[h[i]]-tp[tp1]>tp[tp0]-tp[h[i]])ps0=ps[tp0],antp=tp0;else ps0=ps[tp1],antp=tp1;}
    //    printf("tp0=%d tp1=%d ps0=%d
    ",tp0,tp1,ps0);
        if(antp)add(antp,-1);
        k=query(h[i]);tp0=tp1=0;
    //    printf("i=%d k=%d(antp=%d)
    ",i,k,antp);
        if(n-i>k)
        {
            l=1;r=m;
            while(l<=r)
            {
                int mid=l+r>>1;
                if(query(mid)<=k)tp0=mid,l=mid+1;
                else r=mid-1;
            }
            tp0++;
        }
        if(k>1)
        {
            l=1;r=m;
            while(l<=r)
            {
                int mid=l+r>>1;
                if(query(mid)>=k-1)tp1=mid,r=mid-1;
                else l=mid+1;
            }
        }
        if(!tp0&&tp1)ps1=ps[tp1];else if(tp0&&!tp1)ps1=ps[tp0];
        else if(tp0&&tp1){if(tp[h[i]]-tp[tp1]>tp[tp0]-tp[h[i]])ps1=ps[tp0];else ps1=ps[tp1];}
    //    printf("tp0=%d tp1=%d ps1=%d
    ",tp0,tp1,ps1);
        if(antp)add(antp,1);
    }
    void cz(int cr)
    {
        int a,b;find(cr,b,a);
        dp[cr][0][2]=a;mp[cr]=b;
        if(a)dp[cr][0][0]=abs(tp[h[a]]-tp[h[cr]]);
        
        int ta=mp[a];
        if(ta)
        {
            dp[cr][1][2]=ta;
            dp[cr][1][0]=dp[cr][0][0];  if(ta)dp[cr][1][1]=abs(tp[h[ta]]-tp[h[a]]);
        }
    //    printf("cr=%d a=%lld b=%lld(a=%d ta=%d)
    ",cr,dp[cr][1][0],dp[cr][1][1],a,ta);
        
        for(int i=2;i<=17;i++)
        {
            a=dp[cr][i-1][2];if(!dp[a][i-1][2])break;//
            dp[cr][i][0]=dp[cr][i-1][0]+dp[a][i-1][0];
            dp[cr][i][1]=dp[cr][i-1][1]+dp[a][i-1][1];
            dp[cr][i][2]=dp[a][i-1][2];
        }
    }
    int main()
    {
        n=rdn();
        for(int i=1;i<=n;i++)tp[i]=h[i]=rdn();
        sort(tp+1,tp+n+1);m=unique(tp+1,tp+n+1)-tp-1;
        for(int i=n;i;i--)
        {
            h[i]=lower_bound(tp+1,tp+n+1,h[i])-tp;
            add(h[i],1);ps[h[i]]=i;cz(i);
        }
        X=rdl();ll yX=X;
        for(int i=1;i<=n;i++)
        {
    //        printf("i=%d
    ",i);
            int now=i;ca=0;cb=0;X=yX;
            for(int j=17;j;j--)
    //        {
    //            if(j<=3)printf("X=%lld now=%d j=%d a=%lld b=%lld
    "
    //                ,X,now,j,dp[now][j][0],dp[now][j][1]);
                if(dp[now][j][0]+dp[now][j][1]<=X)
                {
                    X-=dp[now][j][0]+dp[now][j][1];
                    ca+=dp[now][j][0];cb+=dp[now][j][1];
                    if(dp[now][j][2])now=dp[now][j][2];
                }
    //        }
    //        printf("X=%lld now=%d j=0 a=%lld
    ",X,now,dp[now][0][0]);
            if(now&&dp[now][0][0]<=X)ca+=dp[now][0][0];
    //        printf("i=%d ca=%lld cb=%lld
    ",i,ca,cb);
            if(cb)
            {
                double w=(double)ca/cb;
                if(w<tc||(fabs(w-tc)<=eps&&h[i]>h[S]))tc=w,S=i;
            }
    //        printf("i=%d S=%d
    ",i,S);
        }
        printf("%d
    ",S);
        m=rdn();
        while(m--)
        {
            S=rdn();X=rdn();
            int now=S;ca=0;cb=0;
            for(int j=17;j;j--)
    //        {
    //            if(j<=3)printf("X=%lld now=%d j=%d a=%lld b=%lld
    "
    //                ,X,now,j,dp[now][j][0],dp[now][j][1]);
                if(dp[now][j][0]+dp[now][j][1]<=X)
                {
                    X-=dp[now][j][0]+dp[now][j][1];
                    ca+=dp[now][j][0];cb+=dp[now][j][1];
                    if(dp[now][j][2])now=dp[now][j][2];
                }
    //        }
    //        printf("X=%lld now=%d j=0 a=%lld
    ",X,now,dp[now][0][0]);
            if(now&&dp[now][0][0]<=X)ca+=dp[now][0][0];
            printf("%lld %lld
    ",ca,cb);
        }
        return 0;
    }
  • 相关阅读:
    最小生成树——prim
    最短路径——floyd(多源最短路径)
    最短路径——Dijkstra(简易版)
    图的遍历——BFS(队列实现)
    图的遍历——DFS(邻接矩阵)
    图的创建——十字链表
    图的创建——邻接表法
    图的创建——邻接矩阵
    队列——链表实现
    队列——数组实现(循环队列)
  • 原文地址:https://www.cnblogs.com/Narh/p/9382336.html
Copyright © 2011-2022 走看看