*点击

[Poi2004] 旅行问题

题目

Description

John 打算驾驶一辆汽车周游一个环形公路。公路上总共有n车站,每站都有若干升汽油(有的站可能油量为零),每升油可以让汽车行驶一千米。John 必须从某个车站出发,一直按顺时针(或逆时针)方向走遍所有的车站,并回到起点。在一开始的时候,汽车内油量为零,John 每到一个车站就把该站所有的油都带上(起点站亦是如此),行驶过程中不能出现没有油的情况。

任务:判断以每个车站为起点能否按条件成功周游一周。

3<=n<=

0p,d2×10^9

Input

第一行是一个整数n ,表示环形公路上的车站数;

接下来n 行,每行两个整数p_i , d_i pi,di 分别表示表示第i号车站的存油量和第i号车站到下一站的距离。

Output

输出共n行,如果从第i号车站出发,一直按顺时针(或逆时针)方向行驶,能够成功周游一圈,则在第i行输出 TAK,否则输出 NIE。

Sample Input

5
3 1
1 2
5 2
0 1
5 4

Sample Output

TAK
NIE
TAK
NIE
TAK

思路

首先浅观此题,能够周游一圈满足的条件就是,在次过程中油量不为$0$;

这一题比较麻烦的是,顺时针和逆时针都可以走,也就是我们需要正着做一遍,倒着也要做一遍;

我们先看看正着怎么做;

首先破环成列;

那么每一次从第$i$个站到终点($i+n-1$), 就要保证 $p[i]-d[i] +$ 上一次剩余的油量 $\geq 0$ ;

那么我们可以设一个$sum[]$ 数组表示 $p[i]-d[i]$ 的前缀和;

如果在由起点$i$到终点的过程中有 $sum[j]-sum[i-1]<0~~(i<j<=i+n-1) $,那么从 $i$ 出发就不能到达;

所以我们可以维护一个单调队列;

找出 $i$ 到 $i+n-1$ 里的 $min(sum[j])$ 如果$min(sum[j])-sum[i-1]<0$ ,那么从 $i$ 出发就不能到达;

那么如果$min(sum[j])-sum[i-1] \geq 0$ ,最小的 $sum$ 都可以到达,那么从$i$ 到其他点的距离就都可以到达;

这样就$ok$ 了;

那么反着做;

需要注意的是

$d[0]=d[n]$ ;

$sum[i]=sum[i-1]+p[n-i+1]-d[n-i]~$ ;

然后也没什么了;

 

代码

#include<bits/stdc++.h>
#define re register//一片死寂 
typedef long long ll;
using namespace std;
inline ll read()
{
    ll a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
ll n;
ll p[1000010],d[1000010],a[1000010],b[1000010];
ll sum[1000010],f[1000010];
ll q[1000010];
inline void doit()
{
    for(re ll i=1;i<=2*n;i++)
        sum[i]=sum[i-1]+a[i];//统计前缀 
    ll head=1,tail=1;
    for(re ll i=1;i<=2*n;i++)//枚举终点 
    {
        while(head<=tail&&i-q[head]+1>n)//如果队列长度大于 n 
            head++;//踢队头 
        while(head<=tail&&sum[q[tail]]>sum[i])//找一个最小的sum[i] 
            tail--;//踢队尾 
        q[++tail]=i;//入队 
        if(i>=n)//终点大于 n  
        {
            if(sum[q[head]]-sum[i-n]>=0)//判断中途油量不为 0 
                f[i-n+1]=1;//那么从 (i-n+1)这个起点可以到 i  
        }
    }
}
inline void doitagain()
{
    memset(sum,0,sizeof(sum));//重置 
    for(int i=1;i<=2*n;i++)
        sum[i]=sum[i-1]+b[i];//统计前缀 
    ll head=1,tail=1;
    q[tail]=0;//重置 
    for(re ll i=1;i<=2*n;i++)
    {
        while(head<=tail&&i-q[head]+1>n)//如果队列长度大于 n 
            head++;//踢队头 
        while(head<=tail&&sum[q[tail]]>sum[i])//找一个最小的sum[i] 
            tail--;//踢队尾 
        q[++tail]=i;//入队 
        if(i>=n)//终点大于 n 
        {
            if(sum[q[head]]-sum[i-n]>=0)//中途油量不为 0 
                f[2*n-i]=1;// 这个地方自己可以画个样例判断,
                           //我就不多解释了 
        }
    }
}
int main()
{
    n=read();//读入 n  
    for(re ll i=1;i<=n;i++)
    {
        p[i]=read();
        d[i]=read();
        a[i]=p[i]-d[i];//计算差值,方便统计 sum[i] 
        a[i+n]=a[i];//破环成列 
    }
    d[0]=d[n];//破环成列 
    doit();
    for(re ll i=1;i<=n;i++)
    {
        b[i]=p[n-i+1]-d[n-i];//从后往前走 
        b[n+i]=b[i];//破环成列 
    }
    doitagain();
    for(re ll i=1;i<=n;i++)
    if(f[i])//输出 
        puts("TAK");//TAK!!! 
    else
        puts("NIE");//NIE ~( ̄▽ ̄)~* 
    //return 0; 
}

 

posted @ 2020-08-12 21:46  木偶人-怪咖  阅读(238)  评论(0编辑  收藏  举报
*访客位置3D地图 *目录