<二分||线段树>【noip 2012】codevs 1217 借教室

这就是个题面
两种做法啊,二分和线段树
一.二分+前缀和:
从第一天到第n天,一天一天地算,然后二分找有没有不符合的,虽然很暴力,但是可以做啊QAQ
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=1000000+10;
int n,m;
int rr[maxn],cha[maxn],d[maxn],t[maxn],s[maxn];

bool check(int x)
{
    //printf("*%d\n",x);
    memset(cha,0,sizeof(cha));
    for(int i=1;i<=x;++i)
    {
        cha[s[i]]+=d[i];
        cha[t[i]+1]-=d[i];
    }
    int sum=0;
    for(int i=1;i<=n;++i)
    {
        sum+=cha[i];
        if(sum>rr[i]) return false;
    }
    return true;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d",&rr[i]);
    for(int i=1;i<=m;++i) scanf("%d%d%d",&d[i],&s[i],&t[i]);
    int l=1,r=m;
    while(l+1<r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) l=mid;
        else r=mid;
    }
    int ans;
    if(!check(l)) ans=l;
    else ans=r;
    if(r==m&&check(r)) printf("0");
    else 
    {
        printf("-1\n");
        printf("%d",ans);
    }

    return 0;
}

二.线段树
维护区间最小值,每次修改区间,查询是否有最小值小于零
一开始TLE,发现是询问最小值是,不用再写一个ask函数来询问刚刚修改的区间的最小值,而是直接O(1)询问区间1~n的最小值就可以了2333(感觉自己好智障)
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=1e6+10,inf=0xfffffff;
int n,m,ans;
int rr[maxn];
struct hh
{
    int l,r;
    int minn;
    int lazy;
}tree[maxn<<2];

void spread(int i)
{
    int &x=tree[i].lazy;
    if(x)
    {
        tree[i<<1].minn+=x;
        tree[i<<1|1].minn+=x;
        tree[i<<1].lazy+=x;
        tree[i<<1|1].lazy+=x;
        x=0;
    }
}
void add(int l,int r,int x,int i)
{
    int lx=tree[i].l,rx=tree[i].r;
    if(lx>=l&&rx<=r)
    {
        tree[i].lazy-=x;
        tree[i].minn-=x;
        return;
    }
    spread(i); 
    int mid=(lx+rx)>>1;
    if(l<=mid) add(l,r,x,i<<1);
    if(r>mid) add(l,r,x,i<<1|1);
    tree[i].minn=min(tree[i<<1].minn,tree[i<<1|1].minn);
}
void build(int l,int r,int i)
{
    tree[i].l=l;
    tree[i].r=r;
    if(l==r)
    {
        tree[i].minn=rr[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,i<<1);
    build(mid+1,r,i<<1|1);
    tree[i].minn=min(tree[i<<1].minn,tree[i<<1|1].minn);
}
int main()
{
    memset(tree,0,sizeof(tree));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d",&rr[i]);
    build(1,n,1);
    for(int i=1;i<=m;++i)
    {
        int d,s,t;
        scanf("%d%d%d",&d,&s,&t);
        add(s,t,d,1);
        if(tree[1].minn<0)
        {
            ans=i;
            break;
        }
    }
    if(ans) printf("-1\n");
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值