• P5468 P6302 [NOI2019]回家路线


    原题P5468洛谷加强版P6302LOJ3156
    听说原题是用脚造的数据,所以可能无法把代码的锅都测出来,所以还是去加强版吧
    做了好长时间,主要是原版得分玄学,有的时候把代码一个地方改错甚至得了更多的分(当时同时还存在其它的错)?/jk,然后加强版又被卡常/kk

    斜率优化dp+vector 维护凸包+堆维护还没放入凸包的点

    首先想怎么设计状态,一开始想的是 (f_{i,j}) 表示时间 (i),刚达到车站 (j),的最小烦躁值
    但这样显然会产生很多无用状态,就是可能在某一个时间 (i),没有到车站 (j) 的车,那这一个状态就是无用的
    所以改进一下,设 (f_i) 为坐完编号为 (i) 的车时,的最小烦躁值,这样可以由车的编号确定到达时间了
    然后对于所有 (y_i=n) 的车,(min{f_i+q_i}) 即为答案

    然后考虑怎么转移,(f_i)(f_j) 转移而来的条件是,(q_jle p_i,y_j=x_i),所以可以吧防尘写成这样:

    [f_i=min_{q_jle p_i,y_j=x_i}{f_j+A(p_i-q_j)^2+B(p_i-q_j)+C} ]

    那直接暴力转移就是 (O(n^2)) 了,能得 (70),再加上有 (20) 的特殊限制的部分分,那谁还为了最后十分写dp优化啊

    还是要写的,看着有一个平方项,展开的话就有一项是和 (i,j) 都有关,那就能想到斜率优化了
    其实感觉好多斜率优化的题都是用这么一个平方来创造那个 (kx),就是和 (i,j) 都有关的项(比如经典的玩具装箱)
    那就先不考虑限制条件,化简式子:

    [f_i=f_j+Ap_i^2+Aq_j^2-2Ap_iq_j+Bp_i-Bq_j+C ]

    [f_j+Aq_j^2-Bq_j=2Ap_iq_j+(f_i-Ap_i^2-Bp_i-C) ]

    也就是根据斜率优化常见的套路,把等号左边和 (j) 有关的设为 (Y),括号里和 (i) 有关的(包括常数)设为 (b),且 (K=2Ap_i,X=q_j)
    所以维护一个下凸包,最小化斜率即可

    这时的把限制条件考虑回来
    (y_j=x_i) 这个,只要对每个点都维护一个凸包,(f_i) 从第 (x_i) 个凸包转移,转移结束,就加入到第 (y_i) 个凸包里

    这个 (q_jle p_i) 稍微有点麻烦,因为 (p_j<q_j),所以 (p_j<p_i),那我们应该把所有车按 (p) 升序排序来处理
    但这个 (p_j<p_i) 只是说,如果限制条件满足,一定满足他,但满足他不一定满足限制条件一开始没想清楚被这个坑了
    所以此时再维护 (n) 个小根堆,用 (q) 排序,然后对于每个 (i),就把第 (x_i) 个堆中,所有 (q_jle p_i)(j) 都取出,并放入第 (x_i) 个 vector 维护成凸包
    直到发现一个点 (q_j>p_i) 了,那后面的 (j) 也都不符合要求,插入结束
    如果满足 (q_jle p_i)(j) 被插入凸包了,那由于 (p) 不减,后面的 (i) 肯定也满足了这个条件了
    然后 (f_i) 转移完 (i) 就也不是直接插入凸包里,而是插入对应的堆中,等他符合了相应条件再插入凸包

    这也不妨是一个更普适的思路,就是当用一个数据结构维护点东西(比如凸包)来优化dp时,一个状态被转移完,如果还不符合用它转移其它状态的要求,就先用另一个数据结构存起来

    还会发现这样的话 (K,X) 都递增了,可以用之间说的 vector 维护

    这样轻松过了原版和LOJ,但发现在加强版T成了40
    于是愉快开始卡常,一开始是用 set 当堆来维护,然后换成优先队列,稍微快了点,但还是40
    然后又加了行编译优化,依然40,好像没啥用
    也许是stl用的太多,不然一秒 (10^6)(O(mlog m)) 应该挺好过吧,但因为是动态内存也不好避免stl
    于是现学现卖fread,然后边写周末作业边等到半夜,一交最大点997ms卡过果然卡常的最好方式是等人少的时候提交
    submit+=20

    欢迎大家来看看我的代码常数为啥这么大/kk

    #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<queue>
    #include<vector>
    #include<set>
    #include<cstring>
    #define reg register
    #define EN std::puts("")
    #define LL long long
    namespace Fread{
    	char buffer[32768],*cs,*ct;
    	#define get_char (cs==ct&&(ct=(cs=buffer)+fread(buffer,1,32768,stdin),cs==ct)?0:*cs++)
    	inline int read(){
    		register int x=0;
    		register char c=get_char;
    		while(c<'0'||c>'9') c=get_char;
    		while(c>='0'&&c<='9') x=x*10+(c^48),c=get_char;
    		return x;
    	}
    	#undef getc
    }
    using namespace Fread;
    struct data{
    	int x,y;
    	LL p,q;
    //	int id;
    }t[1000006];
    int n,m;
    LL A,B,C;
    LL f[1000005];
    LL X[1000001],Y[1000001];
    std::vector<int>q[1000001];
    struct cmp{
    	inline int operator()(const int &aa,const int &bb){
    		return t[aa].q==t[bb].q?aa>bb:t[aa].q<t[bb].q;
    	}
    };
    struct _insert{
    	int i;
    	inline int operator <(const _insert &a)const{return t[i].q>t[a.i].q;}//按 q 的升序
    };
    std::priority_queue<_insert>insert[1000001];
    inline int cmp(data a,data b){return a.p<b.p;}
    inline LL min(LL a,LL b){return a>b?b:a;}
    //inline LL X(int i){return t[i].q;}
    //inline LL Y(int i){return f[i]+A*t[i].q*t[i].q-B*t[i].q;}
    inline void Insert(int i){
    	reg int size,j,head;//size 为 set insert 的大小,head 为 vector q[x[i]] 最后一个数的下标
    	size=insert[t[i].x].size();
    	if(!size) return;
    	j=insert[t[i].x].top().i;
    	while(size&&t[j].q<=t[i].p){
    		head=q[t[i].x].size()-1;
    		while(head>0){
    			if((Y[q[t[i].x][head]]-Y[q[t[i].x][head-1]])*(X[j]-X[q[t[i].x][head]])
    			>=(Y[j]-Y[q[t[i].x][head]])*(X[q[t[i].x][head]]-X[q[t[i].x][head-1]])) q[t[i].x].pop_back();
    			else break;
    			head--;
    		}
    		q[t[i].x].push_back(j);
    		size--;insert[t[i].x].pop();
    		j=insert[t[i].x].top().i;
    	}
    }
    inline void pop(int i){
    	reg int size=q[t[i].x].size();
    	reg LL K=2*A*t[i].p;
    	while(size>1&&Y[q[t[i].x][1]]-Y[q[t[i].x][0]]<=K*(X[q[t[i].x][1]]-X[q[t[i].x][0]])){
    		size--;
    		q[t[i].x].erase(q[t[i].x].begin());
    	}
    }
    int main(){
    //		std::freopen("P6302_2.in","r",stdin);
    //		std::freopen("tmp.txt","w",stdout);
    	n=read();m=read();A=read();B=read();C=read();
    	for(reg int i=1;i<=m;i++)
    		t[i].x=read(),t[i].y=read(),t[i].p=read(),t[i].q=read(),f[i]=1e10;
    	std::sort(t+1,t+1+m,cmp);
    	f[0]=0;
    	q[1].push_back(0);
    	for(reg int i=1,j;i<=m;i++){//k=2*A*p[i]
    		Insert(i);pop(i);
    		if(q[t[i].x].size()){
    			j=q[t[i].x][0];
    			f[i]=f[j]+A*(t[i].p-t[j].q)*(t[i].p-t[j].q)+B*(t[i].p-t[j].q)+C;
    			insert[t[i].y].push((_insert){i});
    		}
    		X[i]=t[i].q;Y[i]=f[i]+A*t[i].q*t[i].q-B*t[i].q;
    //			if(f[i]<0){
    //				std::printf("i: %d, f[i]: %lld, from: j: %d, f[j]: %lld
    ",i,f[i],j,f[j]);
    //				std::printf("%d: start : %lld, end : %lld
    %d: start : %lld, end : %lld
    ",i,t[i].p,t[i].q,j,t[j].p,t[j].q);
    //				if(t[j].q>t[i].p) std::puts("!!! q[j]>p[i] !!!");
    //				return 0;
    //			}
    	}
    	LL ans=1e18;
    	for(reg int i=1;i<=m;i++)if(t[i].y==n) ans=min(ans,f[i]+t[i].q);
    //		for(reg int i=1;i<=m;i++) std::printf("%lld
    ",f[i]);
    	std::printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    05Linux文件修改
    07Linux基本权限
    09Linux软件安装
    搬家
    web项目和单元测试
    session_start() 对 HTTP_REQUEST扩展/fsockopen函数 的影响
    从不同架构的角度进行web项目的需求分析和系统设计
    程序中的风险控制
    【原创】 书籍推荐
    【转载】10个效果最佳的编程字体
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13238077.html
Copyright © 2020-2023  润新知