补题 DAY1
P2146 [NOI2015] 软件包管理器
给你一棵树,两个操作:
install x
查询路径 \(0\to x\) 上的点权和,并将路径点权赋值为 \(1\)unistall x
查询 \(x\) 的子树点权和,并将子树点权赋值为 \(0\)
树剖板子。恶心。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+3;
int a[maxn],n,m;
vector<int>e[maxn];
void add_edge(int u,int v,bool type=1){
e[u].push_back(v);
if(type) e[v].push_back(u);//无向图
}
#define ls now<<1
#define rs now<<1|1
struct T{
int sum,tag;
}tree[maxn<<2];
void pushup(int now){
tree[now].sum=(tree[ls].sum+tree[rs].sum);
}
void pushdown(int now,int l,int r){
if(tree[now].tag==-1)return;
int mid=(l+r)>>1;
tree[ls].sum=tree[now].tag*(mid-l+1);
tree[rs].sum=tree[now].tag*(r-mid);
tree[ls].tag=tree[rs].tag=tree[now].tag;
tree[now].tag=-1;
}
void build(int now,int l,int r){
tree[now].tag=-1;
if(l==r){tree[now].sum=1;return;}
int mid=(l+r)>>1;
build(ls,l,mid);build(rs,mid+1,r);
pushup(now);
}
void modify(int now,int l,int r,int x,int y,int v){
if(x<=l&&r<=y){
tree[now].sum=v*(r-l+1);
tree[now].tag=v;
return;
}
pushdown(now,l,r);
int mid=(l+r)>>1;
if(x<=mid)modify(ls,l,mid,x,y,v);
if(mid+1<=y)modify(rs,mid+1,r,x,y,v);
pushup(now);
}
int query(int now,int l,int r,int x,int y){
int ans=0;
if(x<=l&&r<=y)return tree[now].sum;
pushdown(now,l,r);
int mid=(l+r)>>1;
if(x<=mid)ans=(ans+query(ls,l,mid,x,y));
if(mid+1<=y)ans=(ans+query(rs,mid+1,r,x,y));
return ans;
}
int siz[maxn],dep[maxn],son[maxn],id[maxn],top[maxn],f[maxn],cnt;
void dfs1(int u,int fa,int depth){
siz[u]=1; f[u]=fa; dep[u]=depth;
int maxx=-maxn;
for(auto v:e[u]){
if(v!=fa){
dfs1(v,u,depth+1);
siz[u]+=siz[v];
if(siz[v]>maxx) maxx=siz[v],son[u]=v;
}
}
}
void dfs2(int u,int t){
id[u]=++cnt; top[u]=t;
if(!son[u]) return;
dfs2(son[u],t);
for(int v:e[u]) if(!id[v]) dfs2(v,v);
}
void modify_tree(int x,int y,int z){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
modify(1,1,n,id[top[x]],id[x],z);
x=f[top[x]];
}
if(id[x]>id[y])swap(x,y);
modify(1,1,n,id[x],id[y],z);
}
int query_tree(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
ans=(ans+query(1,1,n,id[top[x]],id[x]));
x=f[top[x]];
}
if(id[x]>id[y])swap(x,y);
ans=(ans+query(1,1,n,id[x],id[y]));
return ans;
}
signed main(){
cin>>n;
for(int i=1,u;i<n;i++){
cin>>u;u++;
add_edge(i+1,u);
}
dfs1(1,1,1); dfs2(1,1);
build(1,1,n);
cin>>m;
string op;
for(int i=1,x,Ans;i<=m;i++){
cin>>op>>x;x++;
if(op=="install") cout<<query_tree(x,1)<<'\n',modify_tree(1,x,0);
else cout<<siz[x]-query(1,1,n,id[x],id[x]+siz[x]-1)<<'\n', modify(1,1,n,id[x],id[x]+siz[x]-1,1);
}
}
P3313 [SDOI2014] 旅行
S 国有 \(N\) 个城市,编号从 \(1\) 到 \(N\)。城市间用 \(N-1\) 条双向道路连接,满足从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。
为了方便,我们用不同的正整数代表各种宗教,S 国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S 国为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。
在 S 国的历史上常会发生以下几种事件:
CC x c
:城市 \(x\) 的居民全体改信了 \(c\) 教;CW x w
:城市 \(x\) 的评级调整为 \(w\);QS x y
:一位旅行者从城市 \(x\) 出发,到城市 \(y\),并记下了途中留宿过的城市的评级总和;QM x y
:一位旅行者从城市 \(x\) 出发,到城市 \(y\),并记下了途中留宿过的城市的评级最大值。
由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。
对于 \(100\%\) 的数据,\(N,Q \leq10^5,C \leq10^5\)
数据保证对所有 QS
和 QM
事件,起点和终点城市的信仰相同;在任意时刻,城市的评级总是不大于 \(10^4\) 的正整数,且宗教值不大于 \(C\)。
对于每个 \(C\) 建一棵线段树维护区间和,区间 \(\max\),单点修改,动态开点,然后树剖求链和,没了,时间复杂度 \(O(C+n\log^2 n)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+3;
int a[maxn],n,m,w[maxn],c[maxn];
int siz[maxn],dep[maxn],son[maxn],id[maxn],top[maxn],f[maxn],cnt;
vector<int>e[maxn];
void add_edge(int u,int v,bool type=1){
e[u].push_back(v);
if(type) e[v].push_back(u);
}
struct T{
int sum,ls,rs,mx;
}tree[maxn<<5];
int tim,root[maxn],node=100000;
void pushup(int pos){
tree[pos].sum=tree[tree[pos].ls].sum+tree[tree[pos].rs].sum;
tree[pos].mx=max(tree[tree[pos].ls].mx,tree[tree[pos].rs].mx);
}
int modify(int pos,int l,int r,int k,int val){
if(!pos) pos=++cnt;
if(l==r){
tree[pos].sum=tree[pos].mx=val;
return pos;
}
int mid=(l+r)>>1;
if(k<=mid) tree[pos].ls=modify(tree[pos].ls,l,mid,k,val);
else tree[pos].rs=modify(tree[pos].rs,mid+1,r,k,val);
pushup(pos);
return pos;
}
int querysum(int pos,int l,int r,int L,int R){
if(!pos) return 0;
if(L<=l&&r<=R) return tree[pos].sum;
int mid=(l+r)>>1,sum=0;
if(L<=mid) sum+=querysum(tree[pos].ls,l,mid,L,R);
if(mid+1<=R) sum+=querysum(tree[pos].rs,mid+1,r,L,R);
return sum;
}
int querymax(int pos,int l,int r,int L,int R){
if(!pos) return 0;
if(L<=l&&r<=R) return tree[pos].mx;
int mid=(l+r)>>1,sum=0;
if(L<=mid) sum=max(sum,querymax(tree[pos].ls,l,mid,L,R));
if(mid+1<=R) sum=max(sum,querymax(tree[pos].rs,mid+1,r,L,R));
return sum;
}
void dfs1(int u,int fa,int depth){
siz[u]=1; f[u]=fa; dep[u]=depth;
int maxx=-maxn;
for(auto v:e[u]){
if(v!=fa){
dfs1(v,u,depth+1);
siz[u]+=siz[v];
if(siz[v]>maxx) maxx=siz[v],son[u]=v;
}
}
}
void dfs2(int u,int t){
id[u]=++cnt; top[u]=t;
if(!son[u]) return;
dfs2(son[u],t);
for(int v:e[u])
if(!id[v])
dfs2(v,v);
}
int querysuml(int pos,int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
ans=(ans+querysum(pos,1,n,id[top[x]],id[x]));
x=f[top[x]];
}
if(id[x]>id[y])swap(x,y);
ans=(ans+querysum(pos,1,n,id[x],id[y]));
return ans;
}
int querymaxl(int pos,int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
ans=max(ans,querymax(pos,1,n,id[top[x]],id[x]));
x=f[top[x]];
}
if(id[x]>id[y])swap(x,y);
ans=max(ans,querymax(pos,1,n,id[x],id[y]));
return ans;
}
signed main(){
cin>>n>>m; string op;
for(int i=1;i<=node;i++) root[i]=i;
for(int i=1;i<=n;i++) cin>>w[i]>>c[i];
for(int i=1,u,v;i<n;i++) cin>>u>>v,add_edge(u,v);
dfs1(1,1,1); dfs2(1,1);
for(int i=1;i<=n;i++) modify(root[c[i]],1,n,id[i],w[i]);
for(int i=1,x,y;i<=m;i++){
cin>>op>>x>>y;
if(op=="CC"){
modify(root[c[x]],1,n,id[x],0);
modify(root[y],1,n,id[x],w[x]);
c[x]=y;
}else if(op=="CW"){
modify(root[c[x]],1,n,id[x],y);
w[x]=y;
}else if(op=="QS"){
cout<<querysuml(root[c[x]],x,y)<<'\n';
}else{
cout<<querymaxl(root[c[x]],x,y)<<'\n';
}
}
}
P5838 [USACO19DEC] Milk Visits G 弱化版,无修无 \(\max\)。
CF914D Bash and a Tough Math Puzzle
- 给定长度为 \(n\) 的序列 \(a\)。\(m\) 次操作。操作有两种:
1 l r x
:若能在 \(a_l∼a_r\) 中 至多 修改一个数,使得 \(\gcd(a_l,a_{l+1},⋯,a_r)=x\),输出YES
,否则输出NO
。注意我们不需要进行实际的改动。2 i y
:将 \(a_i\) 修改为 \(y\)。
- \(1≤n≤5×10^5\),\(1≤m≤4×10^5\),\(1≤a_i,x,y≤10^9\)。
转化题意,即求 \([l,r]\) 中是否有超过一个数不是 \(x\) 的倍数,是则为 NO
,不是则为 YES
。
于是用线段树维护区间 \(\gcd\),单点修改即可,注意开快读。
时间复杂度 \(O(n\log^2 n)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ls (now<<1)
#define rs (now<<1|1)
//#define int long long
const int maxn=5e5+3;
int tree[maxn<<2],a[maxn];
inline int gcd(int __m, int __n)
{
while (__n != 0)
{
int __t = __m % __n;
__m = __n;
__n = __t;
}
return __m;
}
void pushup(int now){
tree[now]=gcd(tree[ls],tree[rs]);
}
void build(int now,int l,int r){
if(l==r){
tree[now]=a[l];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid); build(rs,mid+1,r);
pushup(now);
}
void modify(int now,int l,int r,int pos,int v){
if(l==r){
tree[now]=v;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) modify(ls,l,mid,pos,v);
else modify(rs,mid+1,r,pos,v);
pushup(now);
}
int ans;
void query(int now,int l,int r,int L,int R,int x){
if(ans>1) return;
if(l==r&&tree[now]%x!=0){ ans++; return; }
if(L<=l&&r<=R)
if(tree[now]%x==0) return;
int mid=(l+r)>>1;
if(L<=mid) query(ls,l,mid,L,R,x);
if(mid+1<=R) query(rs,mid+1,r,L,R,x);
return;
}
int n,m;
signed main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
cin>>m;
for(int i=1,op,x,y,z;i<=m;i++){
cin>>op>>x>>y; ans=0;
if(op&1){
cin>>z;
query(1,1,n,x,y,z);
if(ans>1) cout<<"NO\n";
else cout<<"YES\n";
}else modify(1,1,n,x,y);
}
return 0;
}
P2486 [SDOI2011] 染色
给定一棵 \(n\) 个节点的无根树,共有 \(m\) 个操作,操作分为两种:
- 将节点 \(a\) 到节点 \(b\) 的路径上的所有点(包括 \(a\) 和 \(b\))都染成颜色 \(c\)。
- 询问节点 \(a\) 到节点 \(b\) 的路径上的颜色段数量。
颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221
由三段组成:11
、222
、1
。
对于 \(100\%\) 的数据,\(1 \leq n, m \leq 10^5\),\(1 \leq w_i, c \leq 10^9\),\(1 \leq a, b, u, v \leq n\),\(op\) 一定为 C
或 Q
,保证给出的图是一棵树。
线段树维护区间颜色段数,当前左右端点颜色,然后做区间覆盖区间查询即可,最后套个树剖,时间复杂度 \(O(n\log^2 n)\)。细节巨多,难点在合并。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ls (pos<<1)
#define rs (pos<<1|1)
using namespace std;
const int maxn=1e5+3;
int a[maxn],n,m,c[maxn];
int siz[maxn],dep[maxn],son[maxn],id[maxn],top[maxn],f[maxn],cnt,idx[maxn];
vector<int>e[maxn];
void add_edge(int u,int v,bool type=1){
e[u].push_back(v);
if(type) e[v].push_back(u);
}
struct T{
int sum,lc,rc;
}tree[maxn<<2]; int tag[maxn<<2];
void pushup(int pos){
tree[pos].sum=tree[ls].sum+tree[rs].sum-(tree[ls].rc==tree[rs].lc);
tree[pos].lc=tree[ls].lc,tree[pos].rc=tree[rs].rc;
}
void build(int pos,int l,int r){
if(l==r){
tree[pos].sum=1;
tree[pos].lc=tree[pos].rc=c[idx[l]];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid); build(rs,mid+1,r);
pushup(pos);
}
void pushdown(int pos){
if(!tag[pos]) return;
tag[ls]=tag[rs]=tag[pos];
tree[ls].lc=tree[ls].rc=tree[rs].lc=tree[rs].rc=tag[pos];
tree[ls].sum=tree[rs].sum=1;
tag[pos]=0;
return ;
}
void modify(int pos,int l,int r,int L,int R,int val){
if(L<=l&&r<=R){
tree[pos].sum=1;
tree[pos].lc=tree[pos].rc=val;
tag[pos]=val;
return;
}
pushdown(pos);
int mid=(l+r)>>1;
if(L<=mid) modify(ls,l,mid,L,R,val);
if(mid+1<=R) modify(rs,mid+1,r,L,R,val);
pushup(pos);
}
T querysum(int pos,int l,int r,int L,int R){
if(L<=l&&r<=R) return tree[pos];
int mid=(l+r)>>1;
T sum,q;
pushdown(pos);
if(L<=mid&&mid+1<=R){
sum=querysum(ls,l,mid,L,R);
q=querysum(rs,mid+1,r,L,R);
return {sum.sum+q.sum-(sum.rc==q.lc),sum.lc,q.rc};
}
if(L<=mid) return querysum(ls,l,mid,L,R);
if(mid+1<=R) return querysum(rs,mid+1,r,L,R);
}
void dfs1(int u,int fa,int depth){
siz[u]=1; f[u]=fa; dep[u]=depth;
int maxx=-maxn;
for(auto v:e[u]){
if(v!=fa){
dfs1(v,u,depth+1);
siz[u]+=siz[v];
if(siz[v]>maxx) maxx=siz[v],son[u]=v;
}
}
}
void dfs2(int u,int t){
id[u]=++cnt; top[u]=t; idx[cnt]=u;
if(!son[u]) return;
dfs2(son[u],t);
for(int v:e[u]) if(!id[v]) dfs2(v,v);
}
void modifyl(int x,int y,int val){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
modify(1,1,n,id[top[x]],id[x],val);
x=f[top[x]];
}
if(id[x]>id[y])swap(x,y);
modify(1,1,n,id[x],id[y],val);
}
int queryl(int x,int y){
int ans=0; T p,q,r;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
q=querysum(1,1,n,id[top[x]],id[x]);
p=querysum(1,1,n,id[f[top[x]]],id[f[top[x]]]);
r=querysum(1,1,n,id[top[x]],id[top[x]]);
ans+=q.sum-(p.lc==r.rc);
x=f[top[x]];
}
if(id[x]>id[y]) swap(x,y);
ans=(ans+querysum(1,1,n,id[x],id[y]).sum);
return ans;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>c[i];
for(int i=1,u,v;i<n;i++) cin>>u>>v,add_edge(u,v);
dfs1(1,1,1); dfs2(1,1); build(1,1,n);
for(int i=1,x,y,z;i<=m;i++){
char op;
cin>>op>>x>>y;
if(op=='C') cin>>z, modifyl(x,y,z);
else cout<<queryl(x,y)<<'\n';
}
}
P7735 [NOI2021] 轻重边
小 W 有一棵 \(n\) 个结点的树,树上的每一条边可能是轻边或者重边。接下来你需要对树进行 \(m\) 次操作,在所有操作开始前,树上所有边都是轻边。操作有以下两种:
- 给定两个点 \(a\) 和 \(b\),首先对于 \(a\) 到 \(b\) 路径上的所有点 \(x\)(包含 \(a\) 和 \(b\)),你要将与 \(x\) 相连的所有边变为轻边。然后再将 \(a\) 到 \(b\) 路径上包含的所有边变为重边。
- 给定两个点 \(a\) 和 \(b\),你需要计算当前 \(a\) 到 \(b\) 的路径上一共包含多少条重边。
对于所有测试数据:\(T \le 3\),\(1 \le n, m \le {10}^5\)。
有个很难想到的 trick(% Future_Player %):
初始时给每个点分配不同颜色,然后修改时将路径上的颜色再分配额外的颜色,这样判断轻/重边就转化为了判断边的两端点异/同色。
证明:
设一个计数器 \(c=n+1\),最开始将 \(1\sim n\) 染色为其编号,然后对于每次覆盖操作将路径上的点染色为 \(c\),再 \(c\leftarrow c+1\),可以发现,路径上的边根据判定方式全部变成了重边,而与路径上的点相连的点的颜色全 \(<c\),即与路径上的点相连的边全部变成了轻边,所以是正确的。
然后就和 P2486 染色 差不多了,只是线段树维护的是区间内连续段的长度与区间覆盖,然后树剖一下即可,时间复杂度 \(O(Tn\log^2 n)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define ls (pos<<1)
#define rs (pos<<1|1)
using namespace std;
const int maxn=1e5+3;
int a[maxn],n,m,c[maxn];
int siz[maxn],dep[maxn],son[maxn],id[maxn],top[maxn],f[maxn],cnt,idx[maxn];
vector<int>e[maxn];
void add_edge(int u,int v,bool type=1){
e[u].push_back(v);
if(type) e[v].push_back(u);
}
struct T{
int sum,lc,rc;
}tree[maxn<<2]; int tag[maxn<<2];
void pushup(int pos){
tree[pos].sum=tree[ls].sum+tree[rs].sum+(tree[ls].rc==tree[rs].lc);
tree[pos].lc=tree[ls].lc,tree[pos].rc=tree[rs].rc;
}
void pushdown(int pos,int l,int r){
if(!tag[pos]) return;
int mid=(l+r)>>1;
tag[ls]=tag[rs]=tag[pos];
tree[ls].lc=tree[ls].rc=tree[rs].lc=tree[rs].rc=tag[pos];
tree[ls].sum=mid-l; tree[rs].sum=r-mid-1;
tag[pos]=0;
}
void modify(int pos,int l,int r,int L,int R,int val){
if(L<=l&&r<=R){
tree[pos].sum=r-l;
tree[pos].lc=tree[pos].rc=val;
tag[pos]=val;
return;
}
pushdown(pos,l,r);
int mid=(l+r)>>1;
if(L<=mid) modify(ls,l,mid,L,R,val);
if(mid+1<=R) modify(rs,mid+1,r,L,R,val);
pushup(pos);
}
T querysum(int pos,int l,int r,int L,int R){
if(L<=l&&r<=R) return tree[pos];
int mid=(l+r)>>1;
T sum,q;
pushdown(pos,l,r);
if(L<=mid&&mid+1<=R){
sum=querysum(ls,l,mid,L,R);
q=querysum(rs,mid+1,r,L,R);
return {sum.sum+q.sum+(sum.rc==q.lc),sum.lc,q.rc};
}
else if(L<=mid){
return querysum(ls,l,mid,L,R);
}
else if(mid+1<=R){
return querysum(rs,mid+1,r,L,R);
}
}
void dfs1(int u,int fa,int depth){
siz[u]=1; f[u]=fa; dep[u]=depth;
int maxx=-maxn;
for(auto v:e[u]){
if(v!=fa){
dfs1(v,u,depth+1);
siz[u]+=siz[v];
if(siz[v]>maxx) maxx=siz[v],son[u]=v;
}
}
}
void dfs2(int u,int t){
id[u]=++cnt; top[u]=t; idx[cnt]=u;
if(!son[u]) return;
dfs2(son[u],t);
for(int v:e[u])
if(!id[v])
dfs2(v,v);
}
void modifyl(int x,int y,int val){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
modify(1,1,n,id[top[x]],id[x],val);
x=f[top[x]];
}
if(id[x]>id[y])swap(x,y);
modify(1,1,n,id[x],id[y],val);
}
int queryl(int x,int y){
int ans=0; T p,q,r;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
q=querysum(1,1,n,id[top[x]],id[x]);
p=querysum(1,1,n,id[f[top[x]]],id[f[top[x]]]);
r=querysum(1,1,n,id[top[x]],id[top[x]]);
ans+=q.sum+(p.lc==r.rc);
x=f[top[x]];
}
if(id[x]>id[y]) swap(x,y);
ans=(ans+querysum(1,1,n,id[x],id[y]).sum);
return ans;
}
void mian(){
cin>>n>>m;
for(int i=1,u,v;i<n;i++) cin>>u>>v,add_edge(u,v);
dfs1(1,1,1); dfs2(1,1);
for(int i=1;i<=n;i++) modify(1,1,n,id[i],id[i],i);
for(int i=1,op,x,y,z=n;i<=m;i++){
cin>>op>>x>>y;
if(op==1)
modifyl(x,y,++z);
else
cout<<queryl(x,y)<<'\n';
}
}
int T;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>T;
while(T--){
mian();
cnt=0;
memset(id, 0, sizeof id);
memset(f, 0, sizeof f);
memset(dep, 0, sizeof dep);
memset(siz, 0, sizeof siz);
memset(son, 0, sizeof son);
memset(idx, 0, sizeof idx);
for(int i=1;i<=n;i++) e[i].clear();
}
}
20pts,👈🤣不清空导致的
P2894 [USACO08FEB] Hotel G
第一行输入 \(n,m\),\(n\) 代表有 \(n\) 个房间 \((1\leq n \leq 50,000)\),编号为 \(1 \sim n\),开始都为空房,\(m\) 表示以下有 \(m\) 行操作 \((1\leq m < 50,000)\),以下每行先输入一个数 \(i\) ,表示一种操作:
若 \(i\) 为 \(1\),表示查询房间,再输入一个数 \(x\),表示在 \(1,2,...,n\) 房间中找到长度为 \(x\) 的连续空房,输出连续 \(x\) 个房间中左端的房间号,尽量让这个房间号最小,若找不到长度为 \(x\) 的连续空房,输出 \(0\)。若找得到,在这 \(x\) 个空房间中住上人。
若 \(i\) 为 \(2\),表示退房,再输入两个数 \(x,y\) 代表房间号 \(x \sim x+y-1\) 退房,即让房间为空。
你需要对每个输入 \(1\) 输出对应的答案。
线段树维护区间最长空房,区间长度,前缀最长空房,后缀最长空房,然后没了,难点在 pushup
和 pushdown
,时间复杂度 \(O(m\log n)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ls (pos<<1)
#define rs (pos<<1|1)
int n,m;
const int maxn=5e4+3;
struct T{
int sum,len,lemp,remp;
}tree[maxn<<2];
int tag[maxn<<2];
void pushup(int pos){
tree[pos].len=tree[ls].len+tree[rs].len;
tree[pos].lemp=tree[ls].lemp+(tree[ls].lemp==tree[ls].len?tree[rs].lemp:0);
tree[pos].remp=tree[rs].remp+(tree[rs].remp==tree[rs].len?tree[ls].remp:0);
tree[pos].sum=max(max(tree[ls].sum,tree[rs].sum),tree[ls].remp+tree[rs].lemp);
}
void pushdown(int pos,int l,int r){
if(!tag[pos]) return;
tag[ls]=tag[rs]=tag[pos];
tree[ls].sum=tree[ls].lemp=tree[ls].remp=tree[ls].len*(tag[pos]-1);
tree[rs].sum=tree[rs].lemp=tree[rs].remp=tree[rs].len*(tag[pos]-1);
tag[pos]=0;
}
void build(int pos,int l,int r){
tree[pos].sum=tree[pos].lemp=tree[pos].remp=tree[pos].len=r-l+1;
if(l==r) return;
int mid=(l+r)>>1;
build(ls,l,mid); build(rs,mid+1,r);
}
void modify(int pos,int l,int r,int L,int R,int val){
if(L<=l&&r<=R){
tag[pos]=val;
tree[pos].sum=tree[pos].lemp=tree[pos].remp=tree[pos].len*(tag[pos]-1);
return;
}
pushdown(pos,l,r);
int mid=(l+r)>>1;
if(L<=mid) modify(ls,l,mid,L,R,val);
if(mid+1<=R) modify(rs,mid+1,r,L,R,val);
pushup(pos);
}
int query(int pos,int l,int r,int x){
if(l==r) return 1;
pushdown(pos,l,r);
int mid=(l+r)>>1;
if(tree[ls].sum>=x) return query(ls,l,mid,x);
if(tree[ls].remp+tree[rs].lemp>=x) return mid-tree[ls].remp+1;
return query(rs,mid+1,r,x);
}
signed main(){
cin>>n>>m;
build(1,1,n);
for(int i=1,op,x,y;i<=m;i++){
cin>>op>>x;
if(op==1){
if(tree[1].sum<x){
cout<<"0\n";
continue;
}
int pos=query(1,1,n,x);
cout<<pos<<'\n';
modify(1,1,n,pos,pos+x-1,1);
}else{
cin>>y;
modify(1,1,n,x,x+y-1,2);
}
}
return 0;
}