【noip 2012】开车旅行

13 篇文章 0 订阅
13 篇文章 3 订阅

12年的神题啊写了2天
50 分钟一个n^2预处理的暴力 拿了70分
感觉性价比挺高的
县贴个代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define inf 0x3fffffff
using namespace std;
//by mars_ch
int n,m;
int min1,min2;
struct data
{
    int h,id;
}c[1000005];
int nexta[1000005],nextb[1000005],s;
int disa[100005];
int disb[100005];
int x0;
void dfs(int st,int x,int suma,int sumb,int p,int z)
{
    if(p == 1)  //a 
    {
        if(suma+sumb+abs(c[x].h-c[nexta[x]].h)>z || nexta[x] == 0)
        {
            disa[st]=suma;
            disb[st]=sumb;
            return ;
        }
        suma+=abs(c[x].h-c[nexta[x]].h);
        dfs(st,nexta[x],suma,sumb,2,z);
    }
    else
    {
        if(suma+sumb+abs(c[x].h-c[nextb[x]].h)>z || nextb[x] == 0)
        {
            disa[st]=suma;
            disb[st]=sumb;
            return ;
        }
        sumb+=abs(c[x].h-c[nextb[x]].h);
        dfs(st,nextb[x],suma,sumb,1,z);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&c[i].h);
    }
    for(int i=1;i<=n;i++)
    {
        min1=inf,min2=inf;
        for(int j=i+1;j<=n;j++)
        {
            if(abs(c[i].h-c[j].h)<min1)
            {
                min2=min1;
                nexta[i]=nextb[i];
                min1=abs(c[i].h-c[j].h);
                nextb[i]=j;
            }
            else if(abs(c[i].h-c[j].h)<min2 && abs(c[i].h-c[j].h)!=min1)
            {
                min2=abs(c[i].h-c[j].h);
                nexta[i]=j;
            }
            else if(abs(c[i].h-c[j].h) == min1 && c[j].h>c[nextb[i]].h)
            {
                min2=min1;
                nexta[i]=j;
            }
            else if(abs(c[i].h-c[j].h) == min1 && c[j].h<c[nextb[i]].h)
            {
                min2=min1;
                nexta[i]=nextb[i];
                nextb[i]=j;
            }

        }
    }
    /*for(int i=1;i<=n;i++)
    {
        printf("%d %d\n",nexta[i],nextb[i]);
    }*/
    scanf("%d",&x0);
    /*************第一问************/
    double ans=1.0*0x3fffffff;
    double res=0.0;
    for(int k=1;k<=n;k++)
    {
        dfs(k,k,0,0,1,x0);
        if(disb[k] == 0) res=1.0*0x3fffffff;
        else res=1.0*disa[k]/disb[k];
        if(res<ans)
        {
            ans=res;
            s=k;
        }
    } 
    printf("%d\n",s);
    /***************第二问*****************/ 
    memset(disa,0,sizeof(disa));
    memset(disb,0,sizeof(disb));
    int ss,xx;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&ss,&xx);
        memset(disa,0,sizeof(disa));
        memset(disb,0,sizeof(disb));
        dfs(ss,ss,0,0,1,xx);
        printf("%d %d\n",disa[ss],disb[ss]);    
    } 
}

中间 就一个小细节 就是 注意 精度问题 所以建议除数的时候改成乘法

但是突然想到了个问题:好像没有处理 第一问如果相等时的 海拔高点啊
数据很巧妙啊。。。也挺好改的吧 加上一个判等于,记录一下前驱ok?

然后思考正解 其实 lyd讲过所以知道 set加 倍增。

set很神奇啊 一个自己维护奇奇怪怪的结构体,但是很好用。对于这道题只用找左边两个右边两个 比较一下就ok嗯
倍增不说啥了
mark long long 的神奇输出

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
#include<iostream>
#define maxn 100005
using namespace std;
//by mars_ch
int n;
int disa[maxn][20],disb[maxn][20],f[maxn][20];
int nexta[maxn],nextb[maxn];
struct city
{
    int pos,h;
    bool operator < (const city &b) const{
        return h<b.h;
    }
}c[maxn];
struct data
{
    int pos,dif;
    bool operator <  (const data &b) const
    {
        if(dif!=b.dif) return dif<b.dif;
        return c[pos].h<c[b.pos].h;
    }
}t[6];
int read()
{
    int s=0,f=1;
    char p=getchar();
    while((p<'0' || p>'9') && p!='-') p=getchar();
    if(p == '-') f=-1,p=getchar();
    while(p>='0' && p<='9') s=s*10+p-'0',p=getchar();
    return f*s;
}
set<city> s;
inline void query(int St, int X,long long &ta,long long &tb)  
    {  
        for (int i = 18; ~i; -- i) 
        if (f[St][i] && disa[St][i] + disb[St][i] <= X) 
        {  
            ta += disa[St][i]; tb =tb+ disb[St][i];  
            //cout<<"XXX"<<tb<<" "<<disb[St][i]<<endl;
            X -= disa[St][i] + disb[St][i]; St = f[St][i];  
        }  
        //cout<<"asdasd  "<<tb<<endl;
        int posa = nexta[St]; if (!posa) return;  
        int dis = abs(c[posa].h - c[St].h); if (dis <= X) ta += dis;  
    }     
void find(int x)
{
    set<city> :: iterator it=s.find(c[x]);
    int tot=0;
    if(it!=s.begin())
    {
        --it;t[++tot]=(data){it->pos,abs(it->h-c[x].h)};
        if(it!=s.begin())
        {
            --it;t[++tot]=(data){it->pos,abs(it->h-c[x].h)};++it;
        }
        ++it;
    }
    if((++it)!=s.end())
    {
        t[++tot]=(data){it->pos,abs(it->h-c[x].h)};
        if((++it)!=s.end())
        {
            t[++tot]=(data){it->pos,abs(it->h-c[x].h)};--it;
        }
        --it;
    }
    sort(t+1,t+tot+1);
    nextb[x]=t[1].pos;
    if(tot==1) return;
    nexta[x]=t[2].pos;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        c[i].h=read();
        c[i].pos=i;
    }
    for(int i=n;i>=1;i--)
    {
        s.insert(c[i]);
        if(i!=n)find(i);
    }
    for(int i=1;i<=n;i++)
    {
        int p1=nexta[i],p2=nextb[nexta[i]];
        disa[i][0]=p1?abs(c[i].h-c[p1].h):0;
        disb[i][0]=p2?abs(c[p2].h-c[p1].h):0;
        f[i][0]=p2;
    }
    for(int j=1;j<=18;j++)
    {
        for(int i=1;i<=n;i++)
        {
            f[i][j]=f[f[i][j-1]][j-1];
            disa[i][j]=disa[i][j-1]+disa[f[i][j-1]][j-1];
            disb[i][j]=disb[i][j-1]+disb[f[i][j-1]][j-1];
        }   
    }

        int x0=read();
        int ans = 0;  
        long long ansa = 1e15, ansb = 0ll;  
        for (int i = 1; i <= n; ++ i) {  
            long long ta = 0, tb = 0; 
            query(i, x0,ta,tb);  
        //  cout<<"11111 "<<tb<<endl;
            //printf("   %d   %d   \n",ta,tb);
            if (tb && (!ans || ansa * tb > ansb * ta)) {  
                ansa = ta; ansb = tb; ans = i;  
            }  
        }  
        printf("%d\n",ans);
    /****************第二问********/
    int m;
    m=read();
    for(int i=1;i<=m;i++)
    {
        int ss=read(),xx=read();
        long long ta=0,tb=0;
        query(ss,xx,ta,tb);
//      cout<<ta<<" "<<tb<<endl;
        printf("%lld %lld\n",ta,tb);
     } 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值