JZOJ5427. 【NOIP2017提高A组集训10.25】吃草

本文介绍了一种用于优化草坪清理任务的算法。目标是最少放置“0v0”来清除草坪,并确保总的清理代价最小。通过将问题分解为寻找最小数量的清理单元及优化清理成本两部分,采用贪心算法和动态规划方法进行求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

New Orleans家的后院有很多片草坪,Sullivan负责清理过高的草。但是,Sullivan还有很多家务要干,于是,她想到了一个好方法。
后院总共有n片草坪,第i片草坪投影到数轴上,是一段l[i]到r[i]的闭区间,保证l[i]+r[i]是偶数,l[i]<=r[i]。
Sullivan可以在整点上放0v0来把草吃掉(于是0v0变成了0π0)。如果第i片草坪覆盖了x点上的0π0(l[i]<=x<=r[i]),那么这只0π0就可以吃掉这片草坪里的草。每一片草坪的草需要且只能被一只0π0吃掉。如果一片草坪覆盖了多只0π0,Sullivan可以选择任意一只去吃草。
但是,0π0吃草是有代价的,对于第i片草坪,假如吃草的0π0位于x点上,代价为abs((x-l[i])-(r[i]-x)),即0π0到草坪两端距离之差。
现在,Sullivan想知道:
1.最少需要放几只0v0?
2.在放最少只数的0v0情况下,代价最小是多少?

Input

第一行两个正整数n,t。
第二行到第n+1行,第i+1行两个正整数l[i],r[i]。

Output

第一行一个非负整数,为最小的0v0数量。
如果t=0,没有第二行输出;如果t=1,第二行输出在放最少只数的0v0情况的最小代价。

Sample Input

3 1
1 11
2 4
5 7

Sample Output

2
0

Data Constraint

20% n,l[i],r[i]<=3000 t=0
30% n,l[i],r[i]<=300000 t=0
20% n,l[i],r[i]<=3000 t=1
30% n,l[i],r[i]<=300000 t=1

Hint

在3上与6上各放一只0v0即可

题解

对于第一个问题是可以很轻松地解决,
用贪心的思想,就是按照端点来排序,
能放在同一个就放在同一个集合里面。

对于第二个问题,
其实到两个端点的距离差只有这个区间的中间点有个,
所有,对于每一个区间都只保留它的中间点。
fi表示在第i个点放了0v0,而且在中间点i前面的都处理完了的最小代价。
转移:
枚举一个j,mid=(i+j)/2.
如果中间点在[j,mid]里面,很显然用放在j的那只0v0,
在(mid,i)里面的,就用在i的那一只来吃。

code

#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long
#define N 3000003
#define db double
#define P putchar
#define G getchar
#define mo 998244353
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
ll min(ll a,ll b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}
void writeln(ll x){write(x);P('\n');}
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}

struct node
{
    int l,r;
}a[N];

ll f[N],sum[N],g1[N],g2[N],s;
int n,t,ans,mx,m,g[N],x;

bool cmp(node a,node b){return a.r<b.r;}

int get(int x,int y,int mid)
{
    ll s=0;
    if(x!=0)s=g1[mid]-g1[x]-(sum[mid]-sum[x])*x;else mid=0;
    s+=g2[mid+1]-g2[y]-(sum[y-1]-sum[mid])*(m-y+1);
    return s;
}

int main()
{
    freopen("grass.in","r",stdin);
    freopen("grass.out","w",stdout);
    memset(f,127,sizeof(f));
    memset(g,127,sizeof(g));
    read(n);read(t);
    for(int i=1;i<=n;i++)
        read(a[i].l),read(a[i].r),m=max(m,a[i].r),sum[(a[i].l+a[i].r)>>1]++;

    for(int i=m;i;i--)
        g2[i]=g2[i+1]+sum[i]*(m-i+1);

    for(int i=1;i<=m;i++)
        g1[i]=sum[i]*i+g1[i-1],sum[i]+=sum[i-1];

    sort(a+1,a+1+n,cmp);

    x=1;f[0]=g[0]=0;

    for(int i=1;i<=m;i++)
    {
        for(;x<=n && a[x].r<i;x++)mx=max(mx,a[x].l);

        for(int j=mx;j<i;j++)
            if(g[j]+1<=g[i])
            {
                if(g[i]!=g[j]+1)g[i]=g[j]+1,f[i]=9223372036854775807;
                f[i]=min(f[i],f[j]+get(j,i,(i+j)>>1));
            }
    }

    for(;x<=n;x++)mx=max(mx,a[x].l);
    ans=n;
    for(int i=mx;i<=m;i++)
        ans=min(ans,g[i]);

    writeln(ans);

    if(t==1)
    {
        s=9223372036854775807;
        for(int i=mx;i<=m;i++)
            if(ans==g[i])s=min(f[i]+get(i,m,m),s);
        writeln(2*s);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值