洛谷 1083 (NOIp2012) 借教室——标记永久化线段树 / 差分+二分
题目:https://www.luogu.org/problemnew/show/P1083
听说线段树不标记永久化会T一个点。
注意mn记录的是本层以下、带上标记的min!
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e6+5,INF=1e9+5; int n,m,a[N],L,R,w,tot,ls[N<<1],rs[N<<1],mn[N<<1],laz[N<<1]; bool flag; int rdn() { int ret=0;char ch=getchar(); while(ch>'9'||ch<'0')ch=getchar(); while(ch>='0'&&ch<='9')(ret*=10)+=ch-'0',ch=getchar(); return ret; } void pshp(int cr) { mn[cr]=min(mn[ls[cr]],mn[rs[cr]])-laz[cr];//-laz[cr]!! } void build(int l,int r,int cr) { mn[cr]=INF; if(l==r){mn[cr]=a[l];return;} int mid=l+r>>1; ls[cr]=++tot;build(l,mid,ls[cr]); rs[cr]=++tot;build(mid+1,r,rs[cr]); pshp(cr); } void mdfy(int l,int r,int cr,int lj) { if(l>=L&&r<=R) {laz[cr]+=w;mn[cr]-=w;return;} int mid=l+r>>1; if(L<=mid)mdfy(l,mid,ls[cr],lj+laz[cr]); if(mid<R)mdfy(mid+1,r,rs[cr],lj+laz[cr]); pshp(cr); } int main() { n=rdn();m=rdn(); for(int i=1;i<=n;i++)a[i]=rdn(); tot=1;build(1,n,1);int i; for(i=1;i<=m;i++) { w=rdn();L=rdn();R=rdn(); mdfy(1,n,1,0); if(mn[1]<0)break; } if(i==m+1)printf("0\n"); else printf("-1\n%d\n",i); return 0; }
然而用每次遍历一下整个数组的二分答案也能做到nlogn。
注意不要把c[ ]弄成真的差分,再弄一个yc[ ]每次memcpy,不然比线段树还慢。如果把减弄成加,每次memset成0,就能快很多。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e6+5; int n,m,a[N],c[N],l[N],r[N],d[N]; bool pan(int k) { memset(c,0,sizeof c); for(int i=1;i<=k;i++)c[l[i]]+=d[i],c[r[i]+1]-=d[i]; for(int i=1,sum=0;i<=n;i++) { sum+=c[i]; if(sum>a[i])return true; } return false; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=m;i++)scanf("%d%d%d",&d[i],&l[i],&r[i]); int l=1,r=m,ans=0; while(l<=r) { int mid=l+r>>1; if(pan(mid))ans=mid,r=mid-1; else l=mid+1; } if(!ans)printf("0\n"); else printf("-1\n%d\n",ans); return 0; }