题解
A - 敌兵布阵 (样题)
- 单点更新+区间求和查询
- 线段树
#include<bits/stdc++.h>
using namespace std;
int sum[50010*4];
void buildtree(int l,int r,int k)//k是当前节点的编号
{
if(l==r) {cin>>sum[k]; return;}
int mid=(l+r)/2;//以下是后序遍历部分
buildtree(l,mid,2*k);
buildtree(mid+1,r,2*k+1);
sum[k]=sum[2*k]+sum[2*k+1];
}
///单点更新
void update(int l,int r,int k,int val,int pos)//这里的l,r,k和上面函数里面的含义一样
{
if(l==r) {sum[k]+=val; return;}
int mid=(l+r)/2;
///下面的if...else部分是找pos节点的
if(pos<=mid) update(l,mid,2*k,val,pos);
else update(mid+1,r,2*k+1,val,pos);
sum[k]=sum[2*k]+sum[2*k+1];
}
///区间查询部分
int query(int l,int r,int ql,int qr,int k)
{
int mid=(l+r)/2;
if(l==ql&&r==qr) return sum[k];
if(ql>mid) return query(mid+1,r,ql,qr,2*k+1);
else if(qr<=mid) return query(l,mid,ql,qr,2*k);
else return query(l,mid,ql,mid,2*k)+query(mid+1,r,mid+1,qr,2*k+1);
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int T;
cin>>T;
int CS=1;
string q;
while(T--)
{
int n;
cin>>n;
buildtree(1,n,1);
cout<<"Case "<<CS++<<":"<<endl;
while(cin>>q)
{
if(q[0]=='E') break;
int a,b;
cin>>a>>b;
if(q[0]=='A')
{
update(1,n,1,b,a);
}
else if(q[0]=='S')
{
update(1,n,1,-b,a);
}
else if(q[0]=='Q')
{
cout<<query(1,n,a,b,1)<<endl;
}
}
}
}
B - I Hate It
- 区间最大值查询
- 线段树
- 线段树的基本应用,把求和处理成最大值。
#include<iostream>
#include<cmath>
using namespace std;
const int mx = 200000;
int Maxn[mx*4];
void buildtree(int l,int r,int k)//k是当前节点的编号
{
if(l==r) {cin>>Maxn[k]; return;}
int mid=(l+r)/2;//以下是后序遍历部分
buildtree(l,mid,2*k);
buildtree(mid+1,r,2*k+1);
Maxn[k]=max(Maxn[2*k],Maxn[2*k+1]);
}
///单点更新
void update(int l,int r,int k,int val,int pos)//这里的l,r,k和上面函数里面的含义一样
{
if(l==r) {Maxn[k] = val; return;}
int mid=(l+r)/2;
///下面的if...else部分是找pos节点的
if(pos<=mid) update(l,mid,2*k,val,pos);
else update(mid+1,r,2*k+1,val,pos);
Maxn[k]=max(Maxn[2*k],Maxn[2*k+1]);
}
///区间查询部分
int query(int l,int r,int ql,int qr,int k)
{
int mid=(l+r)/2;
if(l==ql&&r==qr) return Maxn[k];
if(ql>mid) return query(mid+1,r,ql,qr,2*k+1);
else if(qr<=mid) return query(l,mid,ql,qr,2*k);
else return max(query(l,mid,ql,mid,2*k), query(mid+1,r,mid+1,qr,2*k+1));
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int n, m;
string q;
while(cin>>n>>m)
{
buildtree(1,n,1);
while(m--)//m次查询
{
cin >> q;
int a,b;
cin >> a >> b;
if(q[0]=='U')
{
update(1,n,1,b,a);
}
else if(q[0]=='Q')
{
cout<<query(1,n,a,b,1)<<endl;
}
}
}
return 0;
}
D - Billboard
题目:有个高h,宽w的公告板,要贴n张公告,每个公告的长度是k,高度固定为1,公告放的要尽可能靠上并尽可能靠左,每给出一张公告,要求这个公告在满足要求的情况下放在了第几层。
按照线段树的做法的话,因为公告的高度固定为1,可以对公告板的高度进行线段花费,将其现在的宽度值存起来,然后每次遍历从左子树开始往下走,知道走到叶子节点满足要求即可。
将原来的模板结合结构体携带信息。
#include <stdio.h>
#include <iostream>
using namespace std;
int h,w,n;
int ans;
struct node
{
int l,r,n;
} a[1000000];
void buildtree(int l,int r,int i,int w)//建树
{
a[i].l=l;
a[i].r=r;
a[i].n=w;
if(l!=r)
{
int mid=(l+r)/2;
buildtree(l,mid,2*i,w);
buildtree(mid+1,r,2*i+1,w);
}
}
void update(int i,int x)
{
if(a[i].l == a[i].r)//到了叶子节点,叶子节点的值既是层数
{
a[i].n-=x;//该层宽度减少
ans = a[i].l;
return ;
}
if(x<=a[2*i].n)//符合要求搜左子树
update(2*i,x);
else//否则右子树
update(2*i+1,x);
a[i].n = max(a[2*i].n,a[2*i+1].n);//将左右子树里能放的最大长度存入父亲节点,进行更新
}
int main()
{
int k;
while(~scanf("%d%d%d",&h,&w,&n))
{
h = min(h, n);//min!!!!占得高度最大为n,这样就减少了一些情况
buildtree(1,h,1,w);
for(int i = 1; i<=n; i++)
{
scanf("%d",&k);
if(a[1].n>=k)//如果这个公告没有超出公告板的长度,那么才能放入
{
update(1,k);
printf("%d\n",ans);
}
else
printf("-1\n");
}
}
return 0;
}