文章目录
A—Data Structure?
本题找第L小的数,就是找第几个1;
找到后将这个数删除,就是将这个1变成0;
向上更新,两项的和。第n个数就是n(n个1的和)
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <cmath>
#include <map>
using namespace std;
#define ll long long
const ll maxn = 1e7 + 5;
ll sum,n,m;
ll b[maxn*4], a[maxn],ans[maxn];
void push_up(ll rt)
{
b[rt] = b[rt << 1] + b[rt << 1 | 1] ;
}
void Build(ll l,ll r,ll rt)//创建树
{
//如果达到叶节点
if(l == r)
{
b[rt]=1;
return ;
}
ll m=(l+r)>>1;
//左右递归;
Build(l,m,rt<<1);
Build(m+1,r,rt<<1|1);
push_up(rt);
}
void Update(ll L, ll l, ll r, ll rt)
{
if(l == r)//找到第L点;
{
b[rt]=0;
sum += l;
return ;
}
int mid = l + r >> 1;
if(L <= b[rt<<1])//该数是几就应该在第几位,前n项和如果小于l那就说明l不在这区间内
Update(L, l, mid, rt << 1);
else
Update(L-b[rt<<1], mid + 1, r, rt << 1 | 1);
push_up(rt);
}
int main()
{
ll t,x,cnt=0;
cin>>t;
while(t--)
{
cin >> n>>m;
cnt++;
sum=0;
Build(1,n,1);//构建全是1的树(初始化)
for(ll i=0;i<=m-1;i++)//共m组数据
{
cin>>x;
Update(x, 1, n, 1);//查找并修改
}
printf("Case %lld: %lld\n",cnt,sum);
}
return 0;
}
看到网上还有一种更好的方法,这种方法就不用回溯了。边查找变更新。
我也自己敲了遍,确实快了不少。
除了36 ——40这几行发生变化,其余的不发生变化
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <cmath>
#include <map>
using namespace std;
#define ll long long
const ll maxn = 1e7 + 5;
ll sum,n,m;
ll b[maxn*4], a[maxn],ans[maxn];
void push_up(ll rt)//就是简单的向上更新
{
b[rt] = b[rt << 1] + b[rt << 1 | 1] ;
}
void Build(ll l,ll r,ll rt)//创建树
{
//如果达到叶节点
if(l == r)
{
b[rt]=1;
return ;
}
ll m=(l+r)>>1;
//左右递归;
Build(l,m,rt<<1);
Build(m+1,r,rt<<1|1);
push_up(rt);
}
void Update(ll L, ll l, ll r, ll rt)
//b[rt]--,就是减去这个1后的更新操作,
//提前更新,
{
b[rt]-- ;
//
//
//
//每次找出一个数,总和总会减掉1,每搜索到下一行时,就将父节点-1;
if(l == r)
{
sum += l;
return ;
}
int mid = l + r >> 1;
if(L <= b[rt<<1])
Update(L, l, mid, rt << 1);
else
Update(L-b[rt<<1], mid + 1, r, rt << 1 | 1);
}
int main()
{
ll t,x,cnt=0;
cin>>t;
while(t--)
{
cin >> n>>m;
cnt++;
sum=0;
Build(1,n,1);//构建全是1的树(初始化)
for(ll i=0;i<=m-1;i++)//共m组数据
{
cin>>x;
Update(x, 1, n, 1);
}
printf("Case %lld: %lld\n",cnt,sum);
}
return 0;
}
B—KPI
这道题我找到了一种简单的方法:利用队列来模拟出(out)入(in)过程。数组代表第n大的数
因为数组不多的原因,直接c了,但要是数组足够长,这代码就爆了,这代码复杂度O(mn)(应该是吧,我不是很会算) 所以最好使用线段树,我会更新的。
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <cmath>
#include <map>
using namespace std;
const int maxn = 1e7 + 5;
int sum,n,m;
int b[maxn*4];//此题数在b中是从小到大的顺序的排列的,用来找到第n大的数。
int a[maxn],ans[maxn];
queue<int >q;//队列,模拟出入
int direction(int x)//找到x在数组应该b[i]中的位置。以便在数组b中添加此数,或除去此数;
{
int l=0,r=sum;
while(l<r)
{
int mid=l+r>>1;
if(b[mid]>=x)
r=mid;
else
l=mid+1;
}
return l;
}
void in (int x)//在数组中添加此数。
{
int k=direction(x);//找到x在b中的下标
for(int i=sum+1;i>k;i--)
{
b[i]=b[i-1];
}
b[k]=x;
sum++;
}
void out(int x)//在数组中删去此数,并将后面的数往前移动一位
{
int k=direction(x);//找到x在b中的下标
for(int i=k;i<=sum-1;i++)
{
b[i]=b[i+1];
}
sum--;
}
int main()
{
int n,m;
char s[10];
int cot=1;
while(~scanf("%d",&n))
{
while(!q.empty())
q.pop();
sum=0;
printf("Case #%d:\n", cot++);
for(int i=0;i<=n-1;i++)
{
scanf("%s",s);
if(s[0]=='i')
{
scanf("%d",&m);
q.push(m);
in(m);
}
else if(s[0]=='o')//把队头元素出队列
{
int t=q.front();
q.pop();
out(t);
}
else
printf("%d\n",b[sum>>1]);//查询函数
}
}
return 0;
}
在写第三题之前,关于lower_bound的使用我觉得有必要讲一下
C题中lower_bound的补充
用法
int t=lower_bound(a+l , a+r, m) -a;
//函数的作用是数组a中查找[l,r)中区间中值为m的元素。并返回的下标.需要记住后面有个 - a;
特殊情况
1—如果m在区间没有出现过,那就返回第一个比m大的元素的下标;
2—如果区间内有多个m,则返回第一个m的下标。
3—如果m比区间内所有数都大,那么返回r,这时候会越界;小心
时间复杂度为logn,n为数组长度。
C题中erase用法的补充
用法
erase有很多种用法;我也懒得去复制那些专业术语。
直接举例理解:
第一种:
string str = "hello c++! +++";
//erase可以有1个参数,也可以有2个参数。
//当只有一个参数时,意思是从n开始一直删到最后。
str.erase(10); // 从位置pos=10处开始删除,直到结尾
//两个参数时,意思是从n开始删除m个字符
str.erase(6, 4); // 从位置pos=6处开始,删除4个字符
第二种;(就是带().end或者().begin()啥的那种)
删除迭代器[first, last)区间的所有字符,返回一个指向被删除的最后一个元素的下一个字符的迭代器.????
string str = "hello c++! +++";
//当只有一个参数时,意思是删除这个位置的元素(从0开始)
str.erase(str.begin()+10);// 删除"+++"前的一个空格
//当有两个参数时,意思是,从第n个元素开始,删到"字符串的结尾(str.end())"//这个是可以更改的;
str.erase(str.begin() + 10, str.end());// 删除"+++"
C题中unique用法的补充
用法
1、unique是STL中常用的函数,他的功能是去重,相邻间元素的去重(所以使用前一般先排序),此处的删除并不是真正的删除,而是重复元素的位置被不重复的元素给占领了。unique的实质就是不停地把后面不重复的元素不断移动到前面来。(只替换对应元素,其他不变)
2、unique通常和erase一起使用来达到删除重复元素的目的(此时的删除是真正的删除,即从容器中去除重复元素),容器长度也发生了变化
new_end=unique(a.begin(),a.end());(前闭后开不包括a.end()的元素),new_end是第一个重复数字的地址。
例子:
vector<int >a={1,3,3,4,5,6,6,6,7};
new_end=unique(a.begin(),a.end());
//数组为134567667;new_end返回的是第2个6的地址(第一个重复的是6)
a.erase(new_end(),a.end());//删掉后面的元素
//数组为134567;
所以一般写为a.erase(unique(a.begin(),a.end()),a.end());
C—Atlantis
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
using namespace std;
#define ll long long
const int N=100010;
struct segment//用来存线段信息
{
double x,y1,y2;
int d;//区分他是该矩阵前面的线段还是后面的线段
bool operator < (const segment&t)const
{
return x<t.x;
}
//换个方法也能写
// friend bool operator < (segment&a,segment&b)
// {
// return a.x<b.x;
// }
//
}seg [N<<1];//每个矩阵需要存两个线段
//线段树的每个节点 保存的为线段,0号点为y[0]到y[1],以此类推
struct node
{
int l,r;//记录区间的左右
int cnt;//记录区间出现过几次;
double len;//记录这段区间的长度
}tr[N<<3];
vector<double >ys;
int n;
int find(double y)
{
//需要返回vector中第一个>=y的数的下标;
return lower_bound(ys.begin(),ys.end(),y)-ys.begin();
}
void pushup( int u)
{
//例如:假设tr[1].l=0,tr[1].r=1;
// ys[0]为y【0】 到y[1]的距离,ys【1】为y[0]到y[1]的距离
// tr[1].len等于y[0]到y[2]的距离
// y[2]=ys[tr[1].r+1],y[0]=ys[tr[1].l].
if(tr[u].cnt)//计数器>0,证明可以算
tr[u].len=ys[tr[u].r+1]-ys[tr[u].l];//表示整个区间都被覆盖
//该段长度就为右端点 + 1后在ys中的值 - 左端点在ys中的值
//
//如果tr[u].cnt=0;一共有两种情况
//1.完全覆盖,这种情况由modify的第一个if进入
// 这时就意味着把完整的l,r贡给len的部分清除掉
// 留下其他可能存在的子区间对len的贡献,这是一种现象
//2.不完全覆盖:由modify的最后一行的else进入;
// 表示区间并不是被完全覆盖,可能部分被覆盖,需要通过子节点来更新
else if(tr[u].l!=tr[u].r)
{
tr[u].len=tr[u<<1].len+tr[u<<1|1].len;
}
else
tr[u].len=0;//表示叶子节点且该线段完全被覆盖。为无用线段n,长度变为0;·
}
void modify(int u,int l,int r,int d)//表示从线段树中l点到r点的出现次数+d
{
if(tr[u].l>=l&&tr[u].r<=r)//区间被完全覆盖
{
tr[u].cnt+=d;///该区间出现的次数增加d;
pushup(u);//更新len
}
else
{
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid)
modify(u << 1,l,r,d);//左边存在点
if(r>mid)
modify(u << 1 | 1,l,r,d);//右边存在点
pushup(u);
}
}
void build(int u,int l,int r)
{
tr[u]={l,r,0,0};
if(l!=r)
{
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
//后面都为0,不用更新len。
}
}
int main()
{
int T=1;
while(cin>>n,n)//多组输入
{
ys.clear();
int j=0;//一共出现过j个线段
for(int i=0;i<n;i++)//处理输入
{
double x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
seg[j++]={x1,y1,y2,1};//前面出现的线段
seg[j++]={x2,y1,y2,-1};//后面出现的线段
ys.push_back(y1);
ys.push_back(y2);//y轴出现过那些点。
}
sort(seg,seg+j);//线段按x排序
sort(ys.begin(),ys.end());//先排序
ys.erase(unique(ys.begin(),ys.end()),ys.end());//离散化去重
//例子:假设现在有三个不同的y轴点,分为两个线段
//y[0] ~ y[1],y[1] ~ y[2];
//此时ys.size()为3,ys.size() - 2 为 1;
//此时为 build(1, 0, 1);
//有两个点0 和 1,线段树中0号点为y[0] ~ y[1],1号点为y[1] ~ y[2];
build(1,0,ys.size()-2);
double res=0;
for(int i=0;i<j;i++)
{
//根节点的长度即为此时有效线段长度 ,再 * x轴长度即为面积
if (i)res += tr[1].len * (seg[i].x - seg[i - 1].x);
//处理一下该线段的信息,是加上该线段还是消去
//例子:假设进行modify(1,find(10),find(15) - 1,1);
// 假设find(10) = 0,find(15) = 1;
// 此时为modify(1, 0, 0, 1);
// 表示线段树中0号点出现次数加1;
// 而线段树中0号点刚好为线段(10 ~ 15);
// 这就是为什么要进行find(seg[i].y2) - 1 的这个-1操作
modify(1,find(seg[i].y1), find(seg[i].y2) - 1,seg[i].d);
}
printf("Test case #%d\n", T ++ );
printf("Total explored area: %.2lf\n\n", res);
}
return 0;
}
D—覆盖的面积
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<math.h>
using namespace std;
#define ll long long
const int N=100010;
struct segment//用来存线段信息
{
double x,y1,y2;
int d;//区分他是该矩阵前面的线段还是后面的线段
bool operator < (const segment&t)const
{
return x<t.x;
}
}seg [N<<1];//每个矩阵需要存两个线段
//线段树的每个节点 保存的为线段,0号点为y[0]到y[1],以此类推
struct node
{
int l,r;//记录区间的左右
int cnt;//记录区间出现过几次;
double len,len2;//记录这段区间的长度
}tr[N<<3];
vector<double >ys;
int n;
int find(double y)
{
//需要返回vector中第一个>=y的数的下标;
return lower_bound(ys.begin(),ys.end(),y)-ys.begin();
}
void pushup( int u)
{
//例如:假设tr[1].l=0,tr[1].r=1;
// ys[0]为y【0】 到y[1]的距离,ys【1】为y[0]到y[1]的距离
// tr[1].len等于y[0]到y[2]的距离
// y[2]=ys[tr[1].r+1],y[0]=ys[tr[1].l].
if(tr[u].cnt)//计数器>0,证明可以算
tr[u].len=ys[tr[u].r+1]-ys[tr[u].l];//表示整个区间都被覆盖
//该段长度就为右端点 + 1后在ys中的值 - 左端点在ys中的值
//
//如果tr[u].cnt=0;一共有两种情况
//1.完全覆盖,这种情况由modify的第一个if进入
// 这时就意味着把完整的l,r贡给len的部分清除掉
// 留下其他可能存在的子区间对len的贡献,这是一种现象
//2.不完全覆盖:由modify的最后一行的else进入;
// 表示区间并不是被完全覆盖,可能部分被覆盖,需要通过子节点来更新
else if(tr[u].l!=tr[u].r)
{
tr[u].len=tr[u<<1].len+tr[u<<1|1].len;
}
else
tr[u].len=0;
if(tr[u].cnt>=2)
tr[u].len2=ys[tr[u].r+1]-ys[tr[u].l];
else if(tr[u].l==tr[u].r)
{
tr[u].len2=0;
}
else
if(tr[u].cnt==1)
tr[u].len2=tr[u<<1].len+tr[u<<1|1].len;
else
tr[u].len2=tr[u<<1].len2+tr[u<<1|1].len2;
}
void modify(int u,int l,int r,int d)
{
if(tr[u].l>=l&&tr[u].r<=r)//区间被完全覆盖
{
tr[u].cnt+=d;
pushup(u);//更新len
}
else
{
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid)
modify(u << 1,l,r,d);//左边存在点
if(r>mid)
modify(u << 1 | 1,l,r,d);//右边存在点
pushup(u);
}
}
void build(int u,int l,int r)
{
tr[u]={l,r,0,0};
if(l!=r)
{
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
//后面都为0,不用更新len。
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
cin>>n;
ys.clear();
int j=0;
for(int i=0;i<n;i++)
{
double x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
seg[j++]={x1,y1,y2,1};
seg[j++]={x2,y1,y2,-1};
ys.push_back(y1);
ys.push_back(y2);
}
sort(seg,seg+j);
sort(ys.begin(),ys.end());
ys.erase(unique(ys.begin(),ys.end()),ys.end());//离散化去重
build(1,0,ys.size()-2);
double res=0;
for(int i=0;i<j;i++)
{
if(i)
{
res+=tr[1].len2*(double)(seg[i].x-seg[i-1].x);
}
modify(1,find(seg[i].y1), find(seg[i].y2) - 1,seg[i].d);
}
/* int yyy;
yyy=(int )(res*1000);
if(yyy % 10>=5)
yyy=yyy-(yyy%10)+10;
res=(double)yyy/1000;*/
printf("%.2lf\n", res);
}
return 0;
}
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e7+5;
#define ll long long
struct tree
{
int l;//区间左端
int r;//区间右端
int date;//当前区间被覆盖的长度
int add;//统计被覆盖了多少次;
} t[N<<3];
struct point
{
int x;
int y1;
int y2;
int flag;//标记符,用来存左边还是右边,左侧就存1,右边就存-1
} p[N<<1]; //用来存点的
int temp[N][4];//用来存的点的
int val[N<<1];//用来存离散化的
int n;
bool compare (point a,point b)
{
if(a.x<b.x)
return a.x<b.x;
else if(a.x==b.x&&a.flag>b.flag) //flag大的放在前面
return true;
else
return false;
}
void pushup(int rt)
{
if(t[rt].add)
t[rt].date=val[t[rt].r]-val[t[rt].l];//离散化后的左边减右端
else
t[rt].date=t[rt<<1].date+t[rt<<1|1].date;//向上更新(一次也没有覆盖)
}
void build(int rt,int l,int r)
{
//赋值
t[rt].l=l;
t[rt].r=r;
t[rt].date=0;
t[rt].add=0;
if(r == l + 1)
return ;
int mid=l+r>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid,r);
}
void update (int rt,int l,int r,int c)//查找某点并进行更新
{
if(l<=t[rt].l&&r>=t[rt].r)
t[rt].add+=c;
else
{
if(l<t[rt<<1].r)
update(rt<<1,l,r,c);
if(r>t[rt<<1|1].l)
update(rt<<1|1,l,r,c);
}
pushup(rt);
return ;
}
int f()//扫描线,扫x,或扫y
{
sort(val+1,val+(n<<1|1));//排完序,除去相邻间的相同元素,如果用vector可以和erase去掉其中多余元素
//长度就自动为val.size();
int cnt=unique(val+1,val+(n<<1|1))-(val+1);//离散化加去重排序
build(1,1,cnt);
sort(p+1,p+(n<<1|1),compare);//按照从小到大的顺序进行排序
ll ans=0,last=0;//ans用来存答案,last用来存边后的长度
for(int i=1; i<=(n<<1); i++)
{
int l = lower_bound(val+1,val+cnt+1,p[i].y1)-val;
int r = lower_bound(val+1,val+cnt+1,p[i].y2)-val;//查找y对应的离散化
update(1,l,r,p[i].flag);
ans+=abs(last-t[1].date);//绝对值
last=t[1].date;//更新last
}
return ans;
}
int main()
{
int a,b,c,d;
while(~scanf("%d",&n))
{
for(int i=1; i<=n; i++)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
p[i].x=a;
p[i+n].x=c;
p[i].y1=p[i+n].y1=b;
p[i].y2=p[i+n].y2=d;
p[i].flag = 1;
p[i+n].flag = -1;
val[i]=b;
val[i+n]=d;
temp[i][0]=a;
temp[i][1]=b;
temp[i][2]=c;
temp[i][3]=d;
}
ll ans=0;
ans=f();//水平的长
for(int i=1; i<=n; i++)
{
b=temp[i][0];
a=temp[i][1];
d=temp[i][2];
c=temp[i][3];
p[i].x=a;
p[i+n].x=c;
p[i].y1=p[i+n].y1=b;
p[i].y2=p[i+n].y2=d;
p[i].flag = 1;
p[i+n].flag = -1;
val[i]=d;
val[i+n]=b;
}
ans+=f();//竖直的长
printf("%lld\n",ans);
}
return 0;
}
F - The Skyline Problem
#include <cstdio>
#include<iostream>
#include<cstring>
#include <algorithm>
const int maxn = 10005;
using namespace std;
int high[maxn];
int maxx(int a,int b)
{
return a>b?a:b;
}
int main()
{
int L,R,longg,High;
while(scanf("%d%d%d",&L,&High,&R)==3)
{
for(int i=L; i<R; i++)
{
high[i]=maxx(high[i],High);
longg=maxx(longg,i);
}
}
for(int i=1; i<=longg; i++)
{
if(high[i]!=high[i-1])
printf("%d %d ",i,high[i]);
}
printf("%d 0\n",longg+1);
return 0;
}
G—Frequent values
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e6+5;
#define ll long long
struct node
{
int l;//区间左边
int r;//区间右边
int maxs;//最长连续个数
int left;//左边连续个数
int right;//右边连续个数
} t[N<<1];
int n,m,x,y,p[N];
void build(int rt,int l,int r)//建树
{
t[rt].l=l;
t[rt].r=r;
if (l == r)
{
t[rt].left=1;
t[rt].right=1;
t[rt].maxs=1;
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);//初始化为全为1;
if (t[rt<<1].left==mid-l+1&&p[mid]==p[mid+1])
t[rt].left=t[rt<<1].right+t[rt<<1|1].left;//如果左边的所有数都等于右边最左的数(左边连续长度增加)
else
t[rt].left=t[rt<<1].left;
if (t[rt<<1|1].right==r-mid&&p[mid]==p[mid+1])//如果右树的所有数都等于左树最右边的数
t[rt].right=t[rt<<1].right+t[rt<<1|1].left;//右边连续的长度=右儿子+左儿子的连续树个数
else
t[rt].right=t[rt<<1|1].right;//左儿子不是全连续的,右儿子也不是全连续的(就是最优解在中间的情况),就只加右儿子连续部分
if (p[mid]==p[mid+1])//如果右边最左的书等于右边最左的数
{
int u=max(t[rt<<1].maxs,t[rt<<1|1].maxs);//中间的最大连续长度
t[rt].maxs=max(u,t[rt<<1].right+t[rt<<1|1].left);//和左右的最长的最大值作比较;
}
else
t[rt].maxs=max(t[rt<<1].maxs,t[rt<<1|1].maxs);//否则中间一定是最优解
}
int queny(int rt,int l,int r)
{
int max1=1,max2=1,max3=1;
if (t[rt].l>r||t[rt].r<l||t[rt].r<t[rt].l) //若r,l不在范围内
return 0;
if (t[rt].r<=r && t[rt].l>=l)//如果全部包括在内部
return t[rt].maxs;
int mid=(t[rt].r+t[rt].l)>>1;
if (p[mid]==p[mid+1])
max3=min(mid-l+1,t[rt<<1].right)+min(r-mid,t[rt<<1|1].left);//中间的最大连续长度
if (l<=mid)
max1=queny(rt<<1,l,r);//左边的最大连续长度
if (r>mid)
max2=queny(rt<<1|1,l,r);//右边的最大连续长度
max3=max(max3,max(max1,max2));//全部的的最大连续长度
return max3;
}
int main()
{
int i;
while(scanf("%d",&n)&&n)
{
scanf("%d",&m);
for (i=1; i<=n; i++)
scanf("%d",&p[i]);
memset(t,0,sizeof(t));
build(1,1,n);
for(i=1; i<=m; i++)
{
scanf("%d%d",&x,&y);
ll ans=queny(1,x,y);
printf("%lld\n",ans);
}
}
return 0;
}
I—Hotel
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define ls (rt<<1)
#define rs (rt<<1|1)
//1为空房
using namespace std;
const int N=111110;
struct Hotel
{
int lsum;//左侧最长1序列的长度
int rsum;//右侧最长1序列的长度
int sum;//全部最长1序列的长度
int cover;//颜色,cover=1证明这个房间可以用
}hotel[N<<2];
void pushUp(int rt,int m)//区间赋值需要区间长度做参数,所以,这里需要一个m;
{
hotel[rt].lsum=hotel[ls].lsum;//向上继承
hotel[rt].rsum=hotel[rs].rsum;//向上继承
if(hotel[rt].lsum==m-(m>>1))//所谓的左儿子为空事实上是左儿子等于区间长度,m为区间长度,m>>1为右区间长度
hotel[rt].lsum+=hotel[rs].lsum;//需要加上右儿子的左lsum(左边全部是空的)
if(hotel[rt].rsum==(m>>1))//所谓右儿子为空的事实上是右儿子等于区间长度
hotel[rt].rsum+=hotel[ls].rsum;//需要加上左儿子的rsum
hotel[rt].sum=max(hotel[ls].rsum+hotel[rs].lsum,max(hotel[ls].sum,hotel[rs].sum));
//比较:放在左儿子的那个区间,放在右儿子的那个区间,和放在左儿子和右儿子中间(左区间右部分和右区间的左半部分)
}
void Build(int l,int r,int rt)
{
hotel[rt].lsum=hotel[rt].rsum=hotel[rt].sum=r-l+1;//一开始全是1的序列
hotel[rt].cover = -1;//当前区间已经初始化,不用向下更新
if(l == r )
return ;//通过二分法找到相应的点
int m=(l+r)>>1;//找不到就通过二分法继续向下建树
Build(l,m,ls);
Build(m+1,r,rs);
}
void pushDown(int rt,int m)
{
if(hotel[rt].cover!=-1)//在该点值已经变化的情况下,再向下更新
{
hotel[rs].cover=hotel[ls].cover=hotel[rt].cover;
hotel[ls].sum=hotel[ls].lsum=hotel[ls].rsum=hotel[rt].cover?0:m-(m>>1);
hotel[rs].sum=hotel[rs].lsum=hotel[rs].rsum=hotel[rt].cover?0:(m>>1);
hotel[rt].cover=-1;//更新完了就变成-1,证明不必再向下更新,避免重复
}
}
void update(int L,int R,int C,int l,int r,int rt)
{
if(L<=l&&R>=r)//区间[l,r)刚好在L,R之间,就把该地全变成0,证明已经入住,或者把变成r-l+1,证明已经退房
{
hotel[rt].cover=C;
hotel[rt].sum=hotel[rt].rsum=hotel[rt].lsum=hotel[rt].cover?0:r-l+1;
return ;
}
int m=l+r>>1;
pushDown(rt,r-l+1);//向下更新
if(L<=m)
update(L,R,C,l,m,ls);
if(R>m)
update(L,R,C,m+1,r,rs);
pushUp(rt,r-l+1);//回溯向上更新
}
int query(int w,int l,int r,int rt)
{
if(l==r)
return l;
pushDown(rt,r-l+1);
int m=l+r>>1;
if(hotel[ls].sum>=w)
return query(w,l,m,ls);
else
if(hotel[ls].rsum+hotel[rs].lsum>=w)
return m-hotel[ls].rsum+1;
return query(w,m+1,r,rs);
}
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k))
{
Build(1,n,1);
while(k--)
{
int op,a,b;
scanf("%d%d",&op,&a);
if(op==1)
{
if(hotel[1].sum<a)
{
printf("0\n");
continue;
}
int p=query(a,1,n,1);
printf("%d\n",p);
update(p,p+a-1,1,1,n,1);
}
else
{
scanf("%d",&b);
update(a,a+b-1,0,1,n,1);
}
}
}
return 0;
}
J—花神游历各国
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <cmath>
#include <map>
#define ll long long
//typedef long long ll;
using namespace std;
const int maxn = 1e5 + 5;
struct Node
{
int l,r;
ll sum;
int f;
} b[maxn*4];
int a[maxn];
int m,n,cnt=0;
void push_up(int rt)
{
b[rt].sum = b[rt << 1].sum + b[rt << 1 | 1].sum ;
b[rt].f= (b[rt << 1].f && b[rt << 1 | 1].f );
}
void Build(int rt,int l,int r)//创建树
{
//如果达到叶节点
if(l == r)
{
b[rt]= {l,l,a[l],(a[l]<=1)};
}
else
{
b[rt]= {l,r};
int m=(l+r)>>1;
//左右递归;
Build(rt<<1,l,m);
Build(rt<<1|1,m+1,r);
push_up(rt);
}
}
void Update(ll l, ll r, ll rt)
{
if(b[rt].f)//单点修改的优化
return ;
if(b[rt].l == b[rt].r)
{
b[rt].sum=sqrt(b[rt].sum);
b[rt].f=b[rt].sum<=1;
}
else
{
int mid = (b[rt].l + b[rt].r )>> 1;
if(r<= mid)
Update(l, r, rt << 1);
else if(l>mid)
Update( l, r, rt << 1 | 1);
else
{
Update( l, mid, rt << 1 );
Update( mid + 1, r, rt << 1 | 1);
}
push_up(rt);
}
}
ll query(int rt,int l,int r)
{
if(b[rt].l>=l&&b[rt].r<=r) return b[rt].sum;
int mid = (b[rt].l+b[rt].r)>>1;
ll res=0;
if(l<=mid) res+=query(rt<<1,l,r);
if(r>mid) res+=query(rt<<1|1,l,r);
return res;
}
int main()
{
cin>>n;
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
scanf("%d",&m);
Build(1,1,n);
int x,l,r;
while(m--)
{
scanf("%d%d%d",&x,&l,&r);
if(x==1)
printf("%lld\n",query(1,l,r));
else
Update(l,r,1);
}
return 0;
}