vijos1782——借教室(noip2012)

来源:互联网 发布:苹果还是外星人 程序员 编辑:程序博客网 时间:2024/06/10 06:12

这题有两种方法:线段树(90分)和二分(100分)

一、线段树
正常想到线段树,节点维护最小值。重要的是标记下放,只要有更新节点就有下放标记,可以把更新写在pushdown操作中。具体参照代码看。

#include<iostream>#include<algorithm>#include<cstdio>using namespace std;const int maxn=1000005;int x,ql,qr,pos;long long minv[maxn*4],decv[maxn*4]={0};void add(int o,int l,int r)   //相当于建树咯{    if(l==r) minv[o]=x;    else    {        int mid=l+(r-l)/2;        if(pos<=mid) add(o*2,l,mid);        if(pos>mid) add(o*2+1,mid+1,r);        minv[o]=min(minv[o*2],minv[o*2+1]);    }}void pushdown(int o)   //标记下放兼更新minv{    minv[o]-=decv[o];    decv[o*2]+=decv[o];    decv[o*2+1]+=decv[o];    decv[o]=0;}void update(int o,int l,int r){    if(ql<=l&&qr>=r)    {        decv[o]+=x;                 pushdown(o);    }    else    {        pushdown(o);        int mid=l+(r-l)/2;        if(ql<=mid) update(o*2,l,mid); else pushdown(o*2);        if(qr>mid) update(o*2+1,mid+1,r); else pushdown(o*2+1);  //标记下放后,递归到的区间会更新minv,而没递归到的也要更新,两个else不可少。             minv[o]=min(minv[o*2],minv[o*2+1]);         }}int get()     //读入优化{      char c;    do c=getchar(); while (c<'0'||c>'9');      int x=c-'0';      while ('0'<=(c=getchar())&&c<='9') x=x*10+c-'0';     return x; }  int main(){    int n,m;    scanf("%d%d",&n,&m);    for(pos=1;pos<=n;pos++)    {        x=get();        add(1,1,n);    }    for(int i=1;i<=m;i++)    {        x=get(); ql=get(); qr=get();        update(1,1,n);        if(minv[1]<0)        {            cout<<-1<<endl<<i<<endl;            exit(0);        }    }    cout<<0<<endl;}

不加读入优化T了5个点,加了T两个。
尽管不是满分算法,也可以来练习线段树打标记。codevs上读入优化后的可以过。

二、二分法
因为是闭区间,可以二分答案。比较每天需要的教室数和拥有的。用记头尾的方法更新所需教室数。具体见代码。

#include<cstdio>#include<cstring>using namespace std;const int maxn=1000006;int m,n,sum[maxn],d[maxn],s[maxn],j[maxn],a[maxn]={0};int get()     //读入优化{    char ch;    do ch=getchar(); while(ch<'0'||ch>'9');    int x=ch-'0';    while('0'<=(ch=getchar())&&ch<='9') x=x*10+ch-'0';    return x;}int check(int k)    //判断是否可以到第k天{    memset(sum,0,sizeof(sum));    for(int i=1;i<=k;i++)    {        sum[s[i]]+=d[i];        //在头处标记        sum[j[i]+1]-=d[i];      //在尾处去标记    }    int tot=0;    for(int i=1;i<=n;i++)    {        tot+=sum[i];        if(tot>a[i]) return 0; //比较当前所需和拥有数    }    return 1;}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)    {        a[i]=get();    }    for(int i=1;i<=m;i++)    {        d[i]=get(); s[i]=get(); j[i]=get();    }    int l=1,r=m;    int mid,ans=0;    while(l<=r)    {        mid=(l+r)>>1;               if(!check(mid))        {            ans=mid;            r=mid-1;        }        else l=mid+1;    }    if(!ans) printf("0\n");    else printf("-1\n%d\n",ans);}

读入优化是无聊加的,反正可以过。

0 0
原创粉丝点击