前缀&差分大法好。
SHC太大了:线段树维护区间最小值和Lazy_Tag……
本蒟蒻:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
因为是最小的i,所以答案具有可二分性。
所以考虑二分订单,并模拟出剩余的教室数组。
因为是区间加减,所以可以用差分维护,最后前缀和还原。
于是有了个常数巨大的姿势。
又发现差分上操作会有大量重复。
用个栈压着。
复杂度$O(nlogm)$
Code:
#include<cstdio>
#include<cstring>
#define maxn 1000010
using namespace std;
typedef long long LL;
LL r[maxn],d[maxn],s[maxn],t[maxn],S[maxn],last;
int n,m;
int judge(int x)
{
if (x>last) //和栈顶关系
for (int i=last+1;i<=x;i++)
{
S[s[i]]+=d[i];
S[t[i]+1]-=d[i];//S维护差分信息
}
if (x<last)
for (int i=x+1;i<=last;i++)
{
S[s[i]]-=d[i];
S[t[i]+1]+=d[i];
}
int sum=0;
last=x;
for (int i=1;i<=n;i++)
{
sum+=S[i];//前缀和还原差分
if (sum>r[i]) return 0;
}
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lld",&r[i]);
for (int i=1;i<=m;i++)
scanf("%lld%lld%lld",&d[i],&s[i],&t[i]);
int l=1,r=m,ans=0;
last=0;
while (l<=r)
{
int mid=(l+r)>>1;
if (!judge(mid)) {ans=mid;r=mid-1;}
else l=mid+1;
}
if (!ans) puts("0");
else printf("-1\n%d",ans);
return 0;
}
草酸君似乎还有个无敌的$O(n+m)$做法
先把订单全部用上,那么必定有很多天教室数是小于0的。那么我们可以从后往前删除订单。
然后一样是差分模拟,当这天教室数小于0,就把当前订单删除,直到这天教室数合法。
因为是删除订单,所以以前的教室一定合法。
删除订单的时候注意是维护差分数组还是前缀和的值。
Code:
#include<bits/stdc++.h>
#define maxn 1000010
using namespace std;
typedef long long LL;
#define G c=getchar()
inline LL read()
{
LL x=0,f=1;char G;
while(c>57||c<48){if(c=='-')f=-1;G;}
while(c>47&&c<58)x=x*10+c-48,G;
return x*f;
}
LL w[maxn],dt[maxn],c[maxn],l[maxn],r[maxn],cur=0;
int main()
{
int n=read(),m=read(),M=m;
for(int i=1;i<=n;i++)dt[i]=(w[i]=read())-w[i-1];
for(int i=1;i<=m;i++)
c[i]=read(),l[i]=read(),r[i]=read()+1,dt[l[i]]-=c[i],dt[r[i]]+=c[i];
for(int i=1;i<=n;i++)
for(cur+=dt[i];cur<0;m--)
if(l[m]>i)dt[l[m]]+=c[m],dt[r[m]]-=c[m];
else if(r[m]>i)cur+=c[m],dt[r[m]]-=c[m];
if(m==M)puts("0");
else printf("-1\n%d",m+1);
return 0;
}
评论 (0)