• JZOJ 4017. 【雅礼联考DAY01】逃跑(0/1分数规划+单调队列+线段树优化DP)


    JZOJ 4017. 【雅礼联考DAY01】逃跑

    题目

    Description

    Konrad, Delfador 和 Kalenz 一行人又喜闻乐见地被追杀了。
    他们面临的是一条有 N 个地点的路, 他们从 0 号地点出发, 要逃到 N 号地点去。每个地点的战斗都有一定的金币收入 Ai,也有一定的部队损失 Bi。
    为了更好地逃生, Delfador 还弄到了一块传送宝石,这样一行人就能向后传送不超过 L 的距离。从一个地点传送到另一个地点时,Konrad 会选择路径上除起点外的地形指数 Ci 最大的地点进行战斗,地形指数相同时选择最靠后的。
    作为优秀的领导者, Konrad 希望总金币收入与总部队损失的比值最大。

    Input

    第一行,两个整数 N, L。
    接下来 N 行,每行两个整数,分别表示 Ai, Bi, Ci。

    Output

    一行,一个实数,表示答案。
    答案请使用科学计数法输出,保留 9 位小数,具体参见输出样例。指数为 0 时,最后应当输出’0.000000000e+000’。

    Sample Input

    5 3
    1 1 1
    1 2 2
    2 3 1
    1 9 2
    1 1 1

    Sample Output

    3.750000000e-001

    Data Constraint

    在这里插入图片描述

    题解

    • 这题求比值最大,显然是0/1分数规划,一般的思路就是二分答案,
    • 二分一个值 m i d mid mid,判断其是否可行,也就是需要 ∑ a i ∑ b i ≥ m i d frac{sum a_i}{sum b_i}≥mid biaimid
    • 然后通过移项, ∑ a i ≥ m i d × ∑ b i sum a_i≥mid imessum b_i aimid×bi
    • 又有 ∑ a i − m i d × b i ≥ 0 sum a_i-mid imes b_i≥0 aimid×bi0
    • 那么令 i i i号点的贡献为 w i = a i − m i d × b i w_i=a_i-mid imes b_i wi=aimid×bi,则可以通过简单的DP得到50分。
    • f i f_i fi表示到第 i i i号点的最大权值 w w w之和,那么 f i = m a x ( f j + w x ) f_i=max(f_j+w_x) fi=max(fj+wx),其中 j ≥ i − L j≥i-L jiL x x x ( j , i ] (j,i] (j,i] c c c值最大的点,最后看 f n f_n fn是否不小于 0 0 0即可。
    • 但这样的时间复杂度是 O ( n 2 log ⁡ n ) O(n^2log n) O(n2logn)的,需要优化。
    • 考虑用一个单调队列维护地形指数 c c c最大的点,线段数记录每个点的 f i + w x f_i+w_x fi+wx x x x的含义和上面一样,因为 x x x是会变化的,所以我们正是用单调对列维护 f i + w x f_i+w_x fi+wx值的改变。
    • 每次判断 c i ≥ c [ q [ r ] ] c_i≥c[q[r]] cic[q[r]]时, [ q [ r − 1 ] , q [ r ] ) [q[r-1],q[r]) [q[r1],q[r])区间内的 x x x要从 q [ r ] q[r] q[r]变成 i i i,于是用线段树区间修改在这个区间加上 w i − w q [ r ] w_i-w_{q[r]} wiwq[r]
    • 接着队尾加入当前点 i i i,线段树中加入点 i − 1 i-1 i1值为 f i − 1 + w i f_{i-1}+w_i fi1+wi
    • 然后再区间查询线段树 [ m a x ( i − L , 0 ) , i − 1 ] [max(i-L,0),i-1] [max(iL,0),i1]中的最大值,更新 f i f_i fi
    • 注意用long double可能会时超,要改double。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ld double
    #define e 0.0000000001
    #define N 30010
    ld f[N];
    struct
    {
    	ld a,b;
    	int c;
    }a[N];
    int n,L,q[N];
    ld g[N*4],bz[N*4],w[N];
    void add(int v,int l,int r,int x,int y,ld c)
    {
    	if(l==x&&r==y) 
    	{
    		g[v]+=c;
    		bz[v]+=c;
    	}
    	else
    	{
    		bz[v*2]+=bz[v],bz[v*2+1]+=bz[v];
    		g[v*2]+=bz[v],g[v*2+1]+=bz[v];
    		bz[v]=0;
    		int mid=(l+r)/2;
    		if(y<=mid) add(v*2,l,mid,x,y,c);
    		else if(x>mid) add(v*2+1,mid+1,r,x,y,c);
    		else
    		{
    			add(v*2,l,mid,x,mid,c);
    			add(v*2+1,mid+1,r,mid+1,y,c);
    		}
    		g[v]=max(g[v*2],g[v*2+1]);
    	}
    }
    ld find(int v,int l,int r,int x,int y)
    {
    	if(l==x&&r==y) return g[v];
    	bz[v*2]+=bz[v],bz[v*2+1]+=bz[v];
    	g[v*2]+=bz[v],g[v*2+1]+=bz[v];
    	bz[v]=0;
    	int mid=(l+r)/2;
    	if(y<=mid) return find(v*2,l,mid,x,y);
    	else if(x>mid) return find(v*2+1,mid+1,r,x,y);
    	else return max(find(v*2,l,mid,x,mid),find(v*2+1,mid+1,r,mid+1,y));
    }
    int check(ld x)
    {
    	memset(g,0,sizeof(g));
    	memset(bz,0,sizeof(bz));
    	for(int i=1;i<=n;i++) f[i]=-99999999999,w[i]=a[i].a-a[i].b*x;
    	f[0]=0;
    	int le=1,ri=0;
    	for(int i=1;i<=n;i++)
    	{
    		while(le<=ri&&a[i].c>=a[q[ri]].c)
    		{
    			add(1,0,n,q[ri-1],q[ri]-1,w[i]-w[q[ri]]);
    			ri--;
    		}
    		q[++ri]=i;
    		
    		add(1,0,n,i-1,i-1,f[i-1]+w[i]);
    		
    		f[i]=find(1,0,n,max(0,i-L),i-1);
    		
    	}
    	return f[n]>=0;
    }
    int main()
    {
    	int i;
    	scanf("%d%d",&n,&L);
    	for(i=1;i<=n;i++) scanf("%lf%lf%d",&a[i].a,&a[i].b,&a[i].c);
    	ld l=0,r=1000000,ans;
    	while(l+e<=r)
    	{
    		ld mid=(l+r)/2;
    		if(check(mid))
    		{
    			ans=mid;
    			l=mid+e;	
    		}
    		else r=mid-e;	
    	}
    	if(ans>=1&&ans<10)
    	{
    		printf("%.9lfe+000",ans);
    	}
    	else if(ans<1)
    	{
    		int ls=0;
    		while(ans+0.00000001<1) ans*=10,ls++;
    		printf("%.9lfe-",ans);
    		if(ls<10) printf("00%d",ls);
    		else if(ls<100) printf("0%d",ls);
    		else printf("%d",ls);
    	}
    	else
    	{
    		int ls=0;
    		while(ans+e>=10) ans/=10,ls++;
    		printf("%.9lfe+",ans);
    		if(ls<10) printf("00%d",ls);
    		else if(ls<100) printf("0%d",ls);
    		else printf("%d",ls);
    	}
    	return 0;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    Vue
    linux-----docker
    linux基础
    Flask基础
    websocket
    css
    Mysql数据库基础
    IO多路复用
    线程和协程
    sh_02_del关键字
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910045.html
Copyright © 2020-2023  润新知