BZOJ3141: [Hnoi2013]旅行

很厉害的一题
我尝试写过题解发现写的很不清楚所以还是不写了膜题解吧
题解1
题解2


code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

void down(int &x,int y){if(x>y)x=y;}
void up(int &x,int y){if(x<y)x=y;}
void read(int &x)
{
    char c;
    while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0';
}
const int maxn = 510000;
struct node
{
    int x,pre,nex;
    node(){}
    node(int _x,int _pre,int _nex){x=_x;pre=_pre;nex=_nex;}
}a[maxn<<2]; int head[maxn<<1],tail[maxn<<1],len;
int id[maxn],c[maxn],s[maxn];
void ins(int ans,int n,int m,int S,int i)
{
    int k=s[i+1];
    if(ceil((double)abs(S-k)/(m-1))<=ans)
    {
        if(!tail[n+k])
        {
            a[++len]=node(i,0,0);
            head[n+k]=tail[n+k]=len;
        }
        else
        {
            a[++len]=node(i,tail[n+k],0);
            a[tail[n+k]].nex=len;
            tail[n+k]=len;
            int nw=len;
            while(a[nw].pre)
            {
                int p=a[nw].pre;
                if(id[a[p].x]<id[a[nw].x]) break;
                if(a[p].pre) a[a[p].pre].nex=nw;
                else head[n+k]=len;
                a[nw].pre=a[p].pre;
            }
        }
    }
}

int n,m;
int re[maxn],nr;

int main()
{
    read(n); read(m);
    for(int i=1;i<=n;i++)
    {   
        read(id[i]),read(c[i]);
        if(!c[i]) c[i]=-1;
    }
    if(m==1) {printf("%d\n",id[n]); return 0;}
    int l=0;
    for(int i=n;i>=1;i--) 
    {
        s[i]=s[i+1]+c[i];
        if(s[i]==0) l++;
    }
    int ans;
    if(s[1]==0)
    {
        if(l>=m) ans=0;
        else ans=1;
    }
    else ans=ceil(double(abs(s[1]))/double(m));

    if(!ans)
    {
        l--;
        int he=1,ta=0;
        for(int i=1;i<=n;i++)
            if(s[i+1]==0)
            {
                re[++ta]=i; l--;
                while(he<ta&&id[re[ta-1]]>id[re[ta]]&&ta-he+l>=m)
                    re[ta-1]=re[ta],ta--;
            }
        for(int i=he;i<=he+m-2;i++) printf("%d ",id[re[i]]);
        printf("%d\n",id[n]);
        return 0;
    }

    int S=s[1];
    for(int i=1;i<=n-m+1;i++)
        ins(ans,n,m,S,i);

    int lax=0; S=s[1]; nr=0;
    for(int i=1;i<m;i++)
    {
        int mn=INT_MAX,mnx;
        for(int j=n+S-ans;j<=n+S+ans;j++)
        {
            if(ceil((double)abs(j-n)/double(m-i))>ans) continue;
            while(head[j])
            {
                if(a[head[j]].x<=lax)
                {
                    if(head[j]==tail[j]) head[j]=tail[j]=0;
                    else
                    {
                        a[a[head[j]].nex].pre=0;
                        head[j]=a[head[j]].nex;
                    }
                }
                else break;
            }
            if(head[j]) 
            {
                if(mn>id[a[head[j]].x]) 
                    mn=id[a[head[j]].x],mnx=a[head[j]].x;
            }
        }
        re[++nr]=mn;
        S=s[mnx+1];
        lax=mnx;
        if(n-m+i+1<n)
            ins(ans,n,m-i,S,n-m+i+1);
    }
    re[++nr]=id[n];

    for(int i=1;i<nr;i++) printf("%d ",re[i]);
    printf("%d\n",re[nr]);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值