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

55 篇文章 0 订阅

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

Solution

第一问显然
第二问呢?
设f[i]表示到i这个位置而且i必须放,包含了1~i区间的最优答案
区间压成一个点,压到区间中点,这里包含区间就指包含区间中点
那么对于每个i,可以通过一个j转移过来,同时j要合法
转移时答案增加一些数
对于区间在j~i的,偏向j的与j匹配,否则与i匹配
即将j~i的中点分开,两边的区间分别向两边匹配
就这么做事 O(n2)
好像有什么决策单调性??
有大佬知道教我一些
反正数据水, O(n2) 就可以过了

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define fd(i,a,b) for(ll i=a;i>=b;i--)
#define N 301000
#define ll long long
using namespace std;
ll n,t,m,p[N],f[N],s1[N],s2[N],g[N],ans=214748364745751ll;
struct node{
    ll x,y,z;
}a[N];
bool cnt(node x,node y){return x.z<y.z;}
ll get(ll l,ll r,ll x)
{
    l--;
    if(x==r) return (s1[r]-s1[l])*x-(s2[r]-s2[l]);
    else return (s2[r]-s2[l])-(s1[r]-s1[l])*x;
}
int main()
{
    freopen("grass.in","r",stdin);
    freopen("grass.out","w",stdout);
    scanf("%lld%lld",&n,&t);
    fo(i,1,n) scanf("%lld%lld",&a[i].x,&a[i].y),a[i].z=(a[i].x+a[i].y)/2,m=max(m,a[i].y);
    sort(a+1,a+n+1,cnt);
    memset(g,60,sizeof(g));
    memset(f,60,sizeof(f));
    memset(p,0,sizeof(p));
    fo(i,1,n) s1[a[i].z]++,s2[a[i].z]+=a[i].z,p[a[i].y+1]=max(p[a[i].y+1],a[i].x);
    fo(i,1,m+1) s1[i]+=s1[i-1],s2[i]+=s2[i-1],p[i]=max(p[i-1],p[i]);
    g[0]=f[0]=0;
    fo(i,1,m)
    {
        fo(j,p[i],i-1)
        {
            if(g[j]+1>g[i]) break;
            g[i]=g[j]+1;
            f[i]=min(f[i],f[j]+get(j,(i+j)/2,j)+get((i+j)/2+1,i,i));
        }
    }
    fo(i,p[m+1],m) ans=min(ans,g[i]);
    printf("%lld\n",ans);
    if(!t) return 0;
    ll jy=ans;ans=214748364745751ll;
    fo(i,p[m+1],m) if(g[i]==jy) ans=min(ans,f[i]+get(i,m,i));
    printf("%lld\n",ans*2ll);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值