zoukankan      html  css  js  c++  java
  • 【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 imes (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 imes (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 imes (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 imes (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,别分记录字典序和一个数出现的个数。这样复杂度又增加了loglog
    细节还是很多了,调了我一个下午+晚上。
    时间复杂度O(nlog2n)O(nlog^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
    ",f[i]);
    	}
    	printf("%lld",f[n]+t-a[n]);
    	return 0;
    }
    
  • 相关阅读:
    深入理解类的填充规则
    深入理解C++虚函数表
    Java接入图灵机器人,实现与机器人聊天
    flex弹性布局学习笔记
    gulp插件gulp-usemin简单使用
    js基础知识温习:Javascript中如何模拟私有方法
    一次体验很不爽的面试经历
    JavaScript中in操作符(for..in)、Object.keys()和Object.getOwnPropertyNames()的区别
    js基础知识温习:构造函数与原型
    js基础知识温习:js中的对象
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998031.html
Copyright © 2011-2022 走看看