BZOJ_1828_[Usaco2010 Mar]balloc 农场分配_线段树

Description

Input

第1行:两个用空格隔开的整数:N和M * 第2行到N+1行:第i+1行表示一个整数C_i * 第N+2到N+M+1行: 第i+N+1行表示2个整数 A_i和B_i

Output

* 第一行: 一个整数表示最多能够被满足的要求数

Sample Input

5 4
1
3
2
1
3
1 3
2 5
2 3
4 5

Sample Output

3


分析:把每头牛按右端点升序排序,然后能插就插,我们需要维护一下这段区间剩余空间的最小值,如果最小值大于0说明能放进去。

这个我们用线段树来维护。

贪心的证明则比较麻烦,我们需要考虑两个右端点不同的线段的几种可能的覆盖情况。

能够发现一个事情,就是右端点小的那个  要么  能更好的利用所需的区间,要么 {右端点小的放不进去则右端点大的那个也放不进去。}

所以我们不妨让右端点小的那个先试试。(网上亦有题解说右端点相同的按长度排序,其实没有必要,证明同理)

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 100050
#define ls p<<1
#define rs p<<1|1
int t[N<<2],n,m,add[N<<2];
struct C{
    int l,r;
}a[N];
bool cmp(const C &x,const C &y){if(x.r==y.r)return x.l>y.l;return x.r<y.r; }
void build(int l,int r,int p)
{
    if(l==r){
        scanf("%d",&t[p]);
        return ;
    }
    int mid=l+r>>1;
    build(l,mid,ls);build(mid+1,r,rs);
    t[p]=min(t[ls],t[rs]);
}
void pushdown(int p)
{
    int d=add[p];
    if(d)
    {
        t[ls]+=d;add[ls]+=d;
        t[rs]+=d;add[rs]+=d;
        add[p]=0;
    }
}
void update(int l,int r,int x,int y,int c,int p)
{
    if(x<=l&&y>=r){
        t[p]+=c;
        add[p]+=c;
        return;
    }
    int mid=l+r>>1;
    pushdown(p);
    if(x<=mid) update(l,mid,x,y,c,ls);
    if(y>mid) update(mid+1,r,x,y,c,rs);
    t[p]=min(t[ls],t[rs]);
}
int query(int l,int r,int x,int y,int p)
{
    if(x<=l&&y>=r) return t[p];
    int re=1<<30;
    pushdown(p);
    int mid=l+r>>1;
    if(x<=mid) re=min(re,query(l,mid,x,y,ls));
    if(y>mid) re=min(re,query(mid+1,r,x,y,rs));
    t[p]=min(t[ls],t[rs]);
    return re;
}
int main()
{
    scanf("%d%d",&n,&m);
    int i;
    memset(t,0x3f,sizeof(t));
    build(1,n,1);
    for(i=1;i<=m;i++) scanf("%d%d",&a[i].l,&a[i].r);
    sort(a+1,a+m+1,cmp);
    int ans=0;
    for(i=1;i<=m;i++)
    {
        int tmp=query(1,n,a[i].l,a[i].r,1);
        if(tmp>0){
            ans++;
            update(1,n,a[i].l,a[i].r,-1,1);
        }
    }
    printf("%d\n",ans);
}

原文地址:https://www.cnblogs.com/suika/p/8711400.html

时间: 2024-10-07 23:33:43

BZOJ_1828_[Usaco2010 Mar]balloc 农场分配_线段树的相关文章

BZOJ1828 [Usaco2010 Mar]balloc 农场分配

直接贪心,我们把线段按照右端点从小到大排序,然后一个个尝试插入即可... 来证明贪心的正确性: 不妨设贪心得到的答案集合为$S$,最优解的答案集合为$T$ 若$S$不是最优解,那么$S \not= T$,不妨设按照右端点排序后,第一个不同的位置为$i$ 则$S_i \not= T_i$,分情况讨论: (1)$S_i$的左端点在$T_i$的右端点后,由于贪心的步骤这是不可能的 (2)$S_i$的右端点在$T_i$的右端点之前: (2.1)$S_i$的右端点在$T_i$的左端点之前,即$S_i$.$

BZOJ 1828: [Usaco2010 Mar]balloc 农场分配

Description Input 第1行:两个用空格隔开的整数:N和M * 第2行到N+1行:第i+1行表示一个整数C_i * 第N+2到N+M+1行: 第i+N+1行表示2个整数 A_i和B_i Output * 第一行: 一个整数表示最多能够被满足的要求数 题解: 将请求按右端点排序,然后依次添加,用线段树判断是否能添加,不能的放弃.统计个数即可. 代码: #include<cstdio> #include<cstring> #include<algorithm>

敌兵布阵_区间求和问题_线段树 or 树状数组

敌兵布阵 TimeLimit: 2000/1000 MS (Java/Others)  MemoryLimit: 65536/32768 K (Java/Others) 64-bit integer IO format:%I64d Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况.由于采取了某种先进的监测手段,所

BZOJ_1858_[Scoi2010]序列操作_线段树

Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0 3 a b 询问[a, b]区间内总共有多少个1 4 a b 询问[a, b]区间内最多有多少个连续的1 对于每一种询问操作,lxhgww都需要给出回答,聪

BZOJ_2124_等差子序列_线段树+Hash

Description 给一个1到N的排列{Ai},询问是否存在1<=p1<p2<p3<p4<p5<…<pLen<=N (Len>=3), 使得Ap1,Ap2,Ap3,…ApLen是一个等差序列. Input 输入的第一行包含一个整数T,表示组数. 下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开. N<=10000,T<=7 Output 对于每组数据,如果存在一个等差子序列,则输出一行“Y”,否则

BZOJ_4653_[Noi2016]区间_线段树+离散化+双指针

Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri. 对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度.区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值. 求所有合法方案中最小的花费.如果不存在合法的方案,输出 −1.

BZOJ_3252_攻略_线段树+dfs序

Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏<XX 半岛>,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景.所有场景和选择支构成树状 结构:开始游戏时在根节点(共通线),叶子节点为结局.每个场景有一个价值,现在桂马开启攻略之神模式,同 时攻略k次该游戏,问他观赏到的场景的价值和最大是多少(同一场景观看多次是不能重复得到价值的) “为什么你还没玩就知道每个场景的价

BZOJ_4636_蒟蒻的数列_线段树+动态开点

Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将数列[a,b)这个区间中所有比k小的数改为k,他想知 道N次操作后数列中所有元素的和.他还要玩其他游戏,所以这个问题留给你解决. Input 第一行一个整数N,然后有N行,每行三个正整数a.b.k. N<=40000 , a.b.k<=10^9 Output 一个数,数列中所有元素的和 Sample Input 4 2 5 1 9 10 4 6

[bzoj4636]蒟蒻的数列_线段树

蒟蒻的数列 bzoj-4636 题目大意:给定一个序列,初始均为0.n次操作:每次讲一段区间中小于k的数都变成k.操作的最后询问全局和. 注释:$1\le n\le 4\cdot 10^4$. 想法:那个操作就是一个不好好说话的操作,说白了就是对区间的每一个数取max 然后我们对于那个序列建立分治线段树.每个操作我都把它挂在对应的log的点上. n个操作都执行完了之后我们从1号节点深搜,更新答案即可. 最后,附上丑陋的代码... ... #include <iostream> #include