NOIP-2
前言
蒟蒻又滚过来贺题了!!
1.P1719 最大加权矩形
思路:和P1115最大字段和的思路一样,一段以当前端点结尾的子段最大和为当前端点的前缀和减去前面最小的前缀和。至于二维的情况需要压缩一下每一列,从上到下前缀和。这样先枚举底层数,再枚举上层数,最后线性计算,\(O(n^3)\)的复杂度。
代码:
#include<bits/stdc++.h>
using namespace std;
int ans,a[150][150],n;
int sum[1001];
int max_(int a,int b){return a>b?a:b;}
int main(){
scanf("%d",&n);
int i,j,k;
for(i=1;i<=n;++i){
for(j=1;j<=n;++j){
scanf("%d",&a[i][j]);
a[i][j]+=a[i-1][j];
}
}
int minn=0;
for(i=1;i<=n;++i){
for(k=1;k<=i;++k){
int f[150]={0};
memset(sum,0,sizeof sum);
minn=0;
for(j=1;j<=n;++j){
f[j]=a[i][j]-a[i-k][j];
sum[j]=sum[j-1]+f[j];
ans=max_(ans,sum[j]-minn);
minn=min(sum[j],minn);
}
}
}
cout<<ans<<endl;
return 0;
}
2.P1314 [NOIP2011 提高组] 聪明的质监员
思路:分析题意,可以知道,检验结果\(y\)与\(W\)参数成正比,随着\(W\)的增大而减小。所以就可以二分来做,每次判定时先预处理前缀和,再\(O(1)\)处理区间。最后如果比\(s\)大就二分右边,反之左边。这样的时间复杂度就是\(O(n\log n)\) 的。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+100;
int n,m,s;
int w[N],v[N],l[N],r[N];
int sum_w[N],sum_v[N];
int sum;
int check(int x){
memset(sum_w,0,sizeof sum_w);
memset(sum_v,0,sizeof sum_v);
int sum1=0;
sum=0;
for(int i=1;i<=n;i++){
if(w[i]>=x) sum_w[i]=sum_w[i-1]+1,sum_v[i]=sum_v[i-1]+v[i];
else sum_w[i]=sum_w[i-1],sum_v[i]=sum_v[i-1];
}
for(int i=1;i<=m;i++){
sum1+=(sum_w[r[i]]-sum_w[l[i]-1])*(sum_v[r[i]]-sum_v[l[i]-1]);
}
sum=abs(sum1-s);
if(sum1>s) return 1;
else return 0;
}
signed main(){
int mx=-1,mn=2147482647;
cin>>n>>m>>s;
for(int i=1;i<=n;i++){
cin>>w[i]>>v[i];
mx=max(mx,w[i]);
mn=min(mn,w[i]);
}
for(int i=1;i<=m;i++){
cin>>l[i]>>r[i];
}
int l=mn-1,r=mx+2,ans=1e13;
while(l<=r){
int mid=l+r>>1;
if(check(mid)){
l=mid+1;
}else{
r=mid-1;
}
if(sum<ans) ans=sum;
}
cout<<ans<<endl;
return 0;
}
3.P1496 火烧赤壁
解法:这道题考我们使用离散化的能力,但我们反其道而行之!我们可以证明,任意交换两个起点或两个终点不会影响到整个区间的答案,所以我们就分别对起点和终点进行排序,每次加上区间,减去重复的区间即可。
#include<bits/stdc++.h>
using namespace std;
int n;
const int N=1e6;
long long l[N],r[N];
int main(){
cin>>n;
int ans=0;
for(int i=1;i<=n;i++){
cin>>l[i]>>r[i];
}
sort(l+1,l+n+1);
sort(r+1,r+n+1);
for(int i=1;i<=n;i++){
ans+=r[i]-l[i];
if(i+1<=n){
if(r[i]>l[i+1]){
ans-=r[i]-l[i+1];
}
}
}
cout<<ans<<endl;
return 0;
}
3.P1955 [NOI2015] 程序自动分析
解法:并查集先将相等的元素连在一起,再看不等关系是否满足即可。中间用map搞一个映射即可。
#include<bits/stdc++.h>
using namespace std;
int T;
int n;
const int N=1e6+100;
int fa[N];
int l[N],r[N],a[N],b[N],v[N];
int find(int x){
if(x==fa[x]) return x;
else return fa[x]=find(fa[x]);
}
int cnt;
int main(){
cin>>T;
while(T--){
cin>>n;
cnt=0;
int flag=0;
map<int,int> q;
for(int i=1;i<=n;i++){
cin>>l[i]>>r[i]>>v[i];
if(q[l[i]]==0) q[l[i]]=++cnt;
if(q[r[i]]==0) q[r[i]]=++cnt;
l[i]=q[l[i]];
r[i]=q[r[i]];
}
for(int i=1;i<=cnt;i++){
fa[i]=i;
}
for(int i=1;i<=n;i++){
if(v[i]==0) continue;
int fx=find(l[i]);
int fy=find(r[i]);
if(fx!=fy){
fa[fx]=fy;
}
}
for(int i=1;i<=n;i++){
if(v[i]==1) continue;
int fx=find(l[i]);
int fy=find(r[i]);
if(fx==fy){
flag=1;
}
}
if(!flag)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
4.P1884 [USACO12FEB]Overplanting S
扫描线……
#include<bits/stdc++.h>
#define int long long
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=1e6;
int cnt[maxn<<2];
int sum[maxn<<2];
int X[maxn];
struct node{
int h,l,r;
int s;
node(){}
node(int a,int b,int c,int d):l(a),r(b),h(c),s(d){}
bool operator <(const node &cmp)const{
return h<cmp.h;
}
}ss[maxn];
void pushup(int rt,int l,int r){//上传
if(cnt[rt]) sum[rt]=X[r+1]-X[l];//成块加上
else if(l==r) sum[rt]=0;//没有打标记的叶子特殊处理;
else sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int L,int R,int c,int l,int r,int rt){//线段树上传
if(l>=L&&r<=R){
cnt[rt]+=c;
pushup(rt,l,r);
return ;
}
int m=(l+r)>>1;
if(L<=m) update(L,R,c,lson);
if(m<R) update(L,R,c,rson);
pushup(rt,l,r);
}
signed main()
{
int n,m=1;
cin>>n;
for(int i=1;i<=n;i++){
int a,b,c,d;
cin>>a>>b>>c>>d;
a+=1e8;
b+=1e8;
c+=1e8;
d+=1e8;
X[m]=a;
ss[m++]=node(a,c,b,1);//记录横边
X[m]=c;
ss[m++]=node(a,c,d,-1);
}
sort(X+1,X+m+1);
sort(ss+1,ss+m+1);
int k=1;
for(int i=2;i<=m;i++){
if(X[i]!=X[i-1]) X[k++]=X[i];//离散化
}
int ret=0;
for(int i=1;i<m;i++){
int l=lower_bound(X,X+k,ss[i].l)-X;//二分查找
int r=lower_bound(X,X+k,ss[i].r)-X-1;//因为是坐标所以要减一
if(l<r) update(l,r,ss[i].s,1,k,1);
ret+=sum[1]*(ss[i+1].h-ss[i].h);//矩形面积
}
cout<<ret<<endl;
return 0;
}
5.P1083 [NOIP2012 提高组] 借教室
思路:二分找答案,每次判定;
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int diff[1000011],need[1000011],rest[1000011],r[1000011],l[1000011],d[1000011];
bool isok(int x){
memset(diff,0,sizeof(diff));
for(int i=1;i<=x;i++){
diff[l[i]]+=d[i];
diff[r[i]+1]-=d[i];
}
for(int i=1;i<=n;i++){
need[i]=need[i-1]+diff[i];
if(need[i]>rest[i]){
return 0;
}
}
return 1;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>rest[i];
}
for(int i=1;i<=m;i++){
cin>>d[i]>>l[i]>>r[i];
}
int begin=1;
int end=m;
if(isok(m)){
cout<<'0';
return 0;
}
while(begin<end){
int mid=(begin+end)/2;
if(isok(mid)){
begin=mid+1;
}else{
end=mid;
}
}
cout<<"-1"<<endl<<begin;
return 0;
}