这题有两种方法:线段树(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);
}
读入优化是无聊加的,反正可以过。
本文介绍了解决特定问题的两种算法方法——线段树和二分法。通过线段树实现高效的区间更新与查询,展示了如何进行标记下放等关键操作;同时,也介绍了如何使用二分法来寻找最优解,包括如何通过二分答案进行有效验证。
418

被折叠的 条评论
为什么被折叠?



