【APIO2019】路灯
树套树
题目大意:
一条街道有 $n$ 个路灯和 $n+1$ 个位置,每个路灯连接相邻两个位置。两个位置可以通行,必须满足连接两个位置的路灯亮着。
现在有两个操作:
- 修改一盏灯的状态
- 给定位置 $a,b$,询问从 $0$ 到当前,有多少个时刻满足 $a$ 到 $b$ 连通。
题解:
考虑将起点为 $a$ 终点为 $b$ 看做二维平面上的点对。
一个区间 $[l,r]$,其中在位置 $x$ 断开,那么 $[l,x]$ 的每个点到 $[x+1,r]$ 的每个点,都由连通变为不连通。我们对每个点对增加 $t-q$,表示断开。
两个区间 $[l,x]$ 和 $[x+1,r]$ 连通,那么 $[l,x]$ 的每个点到 $[x+1,r]$ 的每个点,都由不连通变为连通。我们对每个点对增加 $q-t$。
观察可得,两个点连通了一段连续的时间,这个点对的值恰好增加了它亮着的时间。
那么查询的时候直接单点查询值即可。如果查询的时候这两个点是连通的,要看做这个时刻不连通处理,以正确计算贡献。
这样问题转化为矩阵加、单点查询问题。可以使用树套树来实现。
需要知道一个段的左、右端点,用 set 维护即可。
时间复杂度 $O((n+q)\log^2 n)$。
Code:
#include<iostream>
#include<set>
using namespace std;
const int N=3e5+5,M=3e7;
int n,q,rt[N];
int d[M],ls[M],rs[M],nod=0;
char s[N];
set<pair<int,int> >st;
void add_2d(int&o,int l,int r,const int&pos,const int&val){
if(!o)o=++nod;
if(l==r)d[o]+=val;else{
const int mid=l+r>>1;
if(pos<=mid)add_2d(ls[o],l,mid,pos,val);else add_2d(rs[o],mid+1,r,pos,val);
d[o]=d[ls[o]]+d[rs[o]];
}
}
void add_1d(int l,int r,const int&L,const int&R,const int&val){
for(int i=l;i<=n;i+=i&-i)add_2d(rt[i],1,n+1,L,val),add_2d(rt[i],1,n+1,R+1,-val);
for(int i=r+1;i<=n;i+=i&-i)add_2d(rt[i],1,n+1,L,-val),add_2d(rt[i],1,n+1,R+1,val);
}
int ask_2d(int o,int l,int r,const int&L,const int&R){
if(!o)return 0;
if(L<=l&&r<=R)return d[o];
const int mid=l+r>>1;int ret=0;
if(L<=mid)ret=ask_2d(ls[o],l,mid,L,R);
if(mid<R)ret+=ask_2d(rs[o],mid+1,r,L,R);
return ret;
}
int ask_1d(int i,int Pos){
int ret=0;
for(;i;i&=i-1)ret+=ask_2d(rt[i],1,n+1,1,Pos);
return ret;
}
void init(){
for(auto it:st){
int l=it.first,r=it.second;
for(int i=l;i<r;++i)
add_1d(i,i,i+1,r,q);
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>q;++n;
int lst=1;
cin>>(s+1);
for(int i=1;i<n;++i){
if(s[i]=='0'){
st.insert(make_pair(lst,i));
lst=i+1;
}
}
st.insert(make_pair(lst,n));
init();
for(int T=1;T<=q;++T){
char op[8];
cin>>op;
if(*op=='t'){
int x;
cin>>x;
if(s[x]=='0'){
s[x]='1';
auto R=st.lower_bound(make_pair(x+1,0));auto L=--R;++R;
int l=L->first,r=R->second;
st.erase(L),st.erase(R);
st.insert(make_pair(l,r));
add_1d(l,x,x+1,r,q-T);
}else{
s[x]='0';
auto it=--st.lower_bound(make_pair(x,1000000));
int l=it->first,r=it->second;
st.erase(it);
st.insert(make_pair(l,x)),st.insert(make_pair(x+1,r));
add_1d(l,x,x+1,r,T-q);
}
}else{
int a,b;
cin>>a>>b;
int ans=ask_1d(a,b);
auto it=--st.lower_bound(make_pair(a,1000000));
if(it->first<=b&&b<=it->second)ans+=T-q;
cout<<ans<<'\n';
}
}
return 0;
}