LGOJ P1083 借教室

LGOJ P1083 借教室

問題の解

线段树模板往上一打……T了一个点……不过o2可以AC……

问题是在比赛中我感觉我会懒得手打线段树……

于是我看了看标签——!!!!

差分数组+二分答案

关于为什么可以用二分:

关于二分答案:
一般来说,二分是个很有用的优化途径,因为这样会直接导致减半运算,而对于能否二分,有一个界定标准:状态的决策过程或者序列是否满足单调性或者可以局部舍弃性。 而在这个题里,因为如果前一份订单都不满足,那么之后的所有订单都不用继续考虑;而如果后一份订单都满足,那么之前的所有订单一定都可以满足,符合局部舍弃性,所以可以二分订单数量。

引自

从第一个订单开始遍历,到某一个订单如果不能安排就会停止,所以可以用二分找到第一个停止的点。

关于为什么用差分数组:

我们的任务是尽量快速地对于一段给定区间进行里面所有数字的加减操作。差分数组diff[]记录的是两个相邻元素之间的\(\delta_{value}\)关系,\(\delta_{value_{cur}}(a_{cur}-a_{cur-1})\),对一段区间的操作可以在\(O(1)\)时间内完成。最后复原数组只需要遍历一遍差分数组,时间复杂度\(O(n)\),n是数组长度。

每一个订单的操作是\(O(1)\),有m个操作所以订单遍历完是\(O(m)\),最后一口气复原订单数组是\(O(n)\)。比暴力的\(O(nm)\) 好多了。

个人觉得二分答案的代码在边边角角的地方容易炸锅,最好要自己编几个样例,对比一下在纸上和在机上的运行过程。

最后找到的是最大的合法订单号,+1就是答案。

二分コード:

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    char c=getchar();int  x=0;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x;
}
const int N=1e6+5;
int n,m,a[N],diff[N],L[N],R[N],D[N];
int cur_arr[N];
inline bool chk(int cur)
{
    memset(diff,0,sizeof(diff));

    for(int i=1;i<=cur;i++)
    {
        diff[L[i]]+=D[i];
        diff[R[i]+1]-=D[i];
    }
    int x=0;
    for(int i=1;i<=n;i++)
    {
        cur_arr[i]=cur_arr[i-1]+diff[i];
        if(a[i]<cur_arr[i])return 0;
    }
    return 1;
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)a[i]=read();
    int l=1,r=m,mid;
    for(int i=1;i<=m;i++)
    {
        D[i]=read();
        L[i]=read();
        R[i]=read();
    }
    int ans=0;
    while(l<=r)
    {
       // cout<<"L&R  "<<l<<' '<<r<<endl;
        mid=(l+r)/2;
        //cout<<"MID_VAL  "<<mid<<' '<<chk(mid)<<endl;
        if(chk(mid))
        {
            l=mid+1;
            ans=max(ans,mid);
        }
        else r=mid-1;
    }
    if(l>=m+1)cout<<"0";
    else cout<<"-1"<<endl<<ans+1;
    

    return 0;
}

线段树的模板是从不知道什么时候写的模板里copy过来的,各种角度看上去都很怪。
セグメント ツリー コード:

#include<bits/stdc++.h>
#define ll long long 
#define mid ((L+R)>>1)
#define ls (o<<1)
#define rs ((o<<1)+1)
#define len (R-L+1)
using namespace std;
inline int read()
{
    char c=getchar();int  x=0;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x;
}
const int N=1e6+5;
const ll INF=0x3f3f3f3f3f3f3f3f;
int n,m,pre_r[N];
ll l, r, dlt;
ll a[N], mini[N << 1], tag[N << 1];

inline void push_up(ll o)
{
    mini[o]=min(mini[ls],mini[rs]);
}

void construct(ll o, ll L, ll R)
{
	tag[o] = 0;
	if (L == R)
	{
        mini[o]=a[L];
		return;
	}
	construct(ls, L, mid);
	construct(rs, mid + 1, R);
	push_up(o);
}

inline void ff(ll o, ll L, ll R,ll d)
{
	tag[o] += d;
    mini[o]+=d;
}

inline void push_down(ll o, ll L, ll R)
{
	ff(ls, L, mid,tag[o]);
	ff(rs, mid + 1, R,tag[o]);
	tag[o] = 0;
}

void update(ll o, ll L, ll R)
{
	if (l <= L && R <= r)
	{
		tag[o] += dlt;
        mini[o]+=dlt;
		return;
	}
	push_down(o, L, R);
	if (l <= mid)update(ls, L, mid);
	if (r > mid)update(rs, mid + 1, R);
	push_up(o);
}

ll query(ll o, ll L, ll R)
{
	if (l <= L && R <= r)
	{
        return mini[o];
	}
	ll res = INF;
	push_down(o, L, R);
	if (l <= mid)
        res=min(res,query(ls, L, mid));
	if (r > mid)
        res=min(res,query(rs, mid + 1, R));
	push_up(o);
	return res;
}


int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)a[i]=read();
    construct(1,1,n);
    bool flag=0;
    for(int i=1;i<=m;i++)
    {
        ll d=read();
        l=read();
        r=read();
        ll cur_mini=query(1,1,n);
        if(cur_mini<d)
        {
            cout<<"-1"<<endl<<i;
            flag=1;
            break;
        }
        dlt=-d;
        update(1,1,n);
    }
    if(flag==0)cout<<0;

    return 0;
}
posted @ 2019-11-09 11:29  miyasaka  阅读(100)  评论(0)    收藏  举报