【JZOJ2320】诡异游戏【dp】【堆】

题目大意:

题目链接:https://jzoj.net/junior/#main/show/2320
在这里插入图片描述


思路:

被C组的题目血虐qwqqwq
在这里插入图片描述
感觉这道题还是不止C组难度的啊。拿到B组去应该也没几个人可以做出来。害得我一下午没有改A组题


把每一个花看成一个点。
首先由一个很显然的性质。如果我们已经取好了[1,x][1,x]的所有花,那么我们下次一定会取一个连续区间[x+1,y][x+1,y]的所有花。也就是说,不会存在某一个时刻我们取的花是不连续的。任意时刻取的花一定是一个左端点为1的连续区间。
所以有一个相对明显的dpdp:设f[i]f[i]表示取完[1,i][1,i]所有的花并且走到点ii所需要的最小时间。
那么就可以枚举这一次取的花的区间[j,i][j,i],那么我们需要求的就是从第jj朵花开始,在第ii朵花结束,并且取完[j,i][j,i]所有花的最少花费时间。
那么如果我们第二次经过一朵花时,这朵花已经种下了,那么就不需要花费任何的时间等待。所以这样子所花费的时间如下图
在这里插入图片描述
其中红色线条表示采摘其中任意一朵花花费的时间。
我们发现,从第一次经过这个点,准备放下花,到我我们最后一次经过这个点,采摘这朵花,所需要的时间(红色线条)正好为2(a[i]a[j])2(a[i]-a[j])
所以无论我们采摘奶一个花,我们花费的时间是与这朵花的位置无关的。
但是如果一朵花所需的等待时间mm是大于2(a[i]a[j])2(a[i]-a[j])的话,那么我们就得等待m2(a[i]a[j])m-2(a[i]-a[j])秒。也就是说,采摘[l,r][l,r]的花,我们需要花费时间为max(a[r]a[l],m)max(a[r]-a[l],m)
所以这样我们就可以轻松的设计出一个O(n2)O(n^2)dpdp
f[i]=min(f[i],f[j1]+a[i]a[j1]+max(2×(a[i]a[j]),m))f[i]=min(f[i],f[j-1]+a[i]-a[j-1]+max(2\times (a[i]-a[j]),m))
其中a[i]a[j1]a[i]-a[j-1]是从上一个结束的花(j1)(j-1)ii的时间。
那么这样就可以获得50pts50pts的高分。
考虑如何优化这个dpdp。我们发现这个dpdp最关键的地方就是maxmax,如果我们知道谁大谁小就可以把这个maxmax省掉了。
也就是说
2×(a[i]a[j])>m2\times (a[i]-a[j])>m时,原方程为
f[i]=min(f[i],f[j1]+a[i]a[j1]+2×(a[i]a[j]))f[i]=min(f[i],f[j-1]+a[i]-a[j-1]+2\times (a[i]-a[j]))
化简
f[i]=min(f[i],f[j1]a[j1]2a[j]+3a[i])f[i]=min(f[i],f[j-1]-a[j-1]-2a[j]+3a[i])
同理,当m>2×(a[i]a[j])m>2\times (a[i]-a[j]),原方程为
f[i]=min(f[i],f[j1]+a[i]a[j1]+m)f[i]=min(f[i],f[j-1]+a[i]-a[j-1]+m)
我们发现,在枚举ii时,3a[i]3a[i]a[i]a[i]都是固定的,所以我们就只需要分别维护f[j1]a[j1]2a[j]f[j-1]-a[j-1]-2a[j]f[j1]+a[i]a[j1]f[j-1]+a[i]-a[j-1]的最小值即可。
同时,容易发现在ii递增的过程中,满足2(a[i]a[j])>m2(a[i]-a[j])>m的花jj一定是一个区间[1,x][1,x]xx是单调不降的。所以我们就只要在把ii每往后挪一位时,利用单调不降的性质,维护好对应的有哪一些花是满足2(a[i]a[j])>m2(a[i]-a[j])>m,又有哪一些花是满足2(a[i]a[j])<m2(a[i]-a[j])<m的,然后分别用两个堆维护一下它们的最小值,然后取minmin即可。
注意堆需要支持删除任意数字,那么就只能手写可删除堆了。
同时花的下标达到了10910^9级别,所以需要开两个mapmap,别分记录字典序和一个数出现的个数。这样复杂度又增加了log\log
细节还是很多了,调了我一个下午+晚上。
时间复杂度O(nlog2n)O(n\log^2 n)。比较卡,最好吸氧(狗头保命


代码:

#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const ll N=100010,Inf=1e18;
ll n,t,m,j,a[N],f[N];

struct Heap
{
    ll a[N],tot;
    map<ll,ll> way,cnt;
    //ll cnt[N];
    
    ll size()
    {
        return tot;
    }
    
    ll top()
    {
    	if (!tot) return Inf;
        return a[1];  
    }
    
    void push(ll x) 
    {
        cnt[x]++;
        if (cnt[x]>1) return;
        a[++tot]=x;
        ll fa=(tot>>1),k=tot;
        way[x]=way[a[fa]]*2+(tot&1);
        while (a[fa]>a[k]&&fa)
        {
            swap(way[a[k]],way[a[fa]]);
            swap(a[k],a[fa]);
            k=fa; fa>>=1; 
        }
    }
    
    void pop(ll x)
    {
        cnt[a[x]]--;
        if (cnt[a[x]]) return;
        swap(way[a[x]],way[a[tot]]);
        swap(a[x],a[tot]); 
        way[a[tot]]=0;
        a[tot]=0;
        tot--;      
        ll son=(x<<1);
        while ((a[son]<a[x]&&son<=tot)||(a[son+1]<a[x]&&son+1<=tot))      
        {
            if (a[son+1]<a[son]&&son+1<=tot) son++;
            swap(way[a[x]],way[a[son]]);
            swap(a[x],a[son]);
            x=son; son<<=1; 
        }
    }
}q1,q2; 

int main()
{
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	scanf("%lld%lld%lld",&n,&t,&m); n++;
	for (ll i=2;i<=n;i++)
		scanf("%lld",&a[i]);
	f[1]=0; j=2;
	for (ll i=2;i<=n;i++)
	{
		q2.push(f[i-1]-a[i-1]+m);
		for (j<=i;2LL*(a[i]-a[j])>m;j++)
		{
			q2.pop(q2.way[f[j-1]-a[j-1]+m]);
			q1.push(f[j-1]-a[j-1]-2*a[j]);
		}
		f[i]=min(q1.top()+3*a[i],q2.top()+a[i]);
		//if (i==30) printf("%lld\n",f[i]);
	}
	printf("%lld",f[n]+t-a[n]);
	return 0;
}
posted @ 2019-09-07 21:55  全OI最菜  阅读(117)  评论(0编辑  收藏  举报