【题解】CF1523G Try Booking
比较清新的数据结构体。
首先观察到 \(n\) 比较小,考虑多个 $\log $ 或 根号算法。
先考虑固定 \(x\) 怎么做,我们可以写一个 calc(l,r)
函数表示时间区间在 \([l,r]\) 内的代价是多少,每次求出区间 \([l,r]\) 中编号最小的日程安排 \((x,y)\),加上它的贡献,然后递归计算 calc(l,x-1)
和 calc(y+1,r)
。
手算一下发现当 \(x\) 递减时,相当于动态加入新的区间。
相当于我们只需要支持插入区间,在线查询 \(l\le (x,y) \le r\) 中编号最小的日程安排 \((x,y)\) 。这是个二维问题,直接树状数组套动态开点线段树维护,插入和查询的时间复杂度都是 \(\mathcal{O}(\log ^2 n)\) 。
然后对于每个 \(x\) ,调用 calc(1,n)
函数计算答案即可。
以下是时间复杂度分析。
已知 \(x\) ,则 calc(l,r)
向下递归的区间至少减少 \(x\) ,我们将每次调用的 calc(l,r)
挂在这次调用加入贡献的日程安排上,发现调用次数是 \(\mathcal{O}(\dfrac{n}{x})\) 级别的。
所以总的时间复杂度是 \(\mathcal{O}(n\log^2 n\ln n+m\log^2 n)\) ,\(300ms\) 稳过。这样我们就解决了这道 3200 的 DS 题。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
#define inf 0x3f3f3f3f
using namespace std;
int n,m,ll[N],rr[N],rt[N],tot,p[N],ans[N];
struct node{
int l,r,mn;
}a[N*200];
#define ls a[x].l
#define rs a[x].r
#define S a[x].mn
void ins(int &x,int l,int r,int pos,int val){
if(!x)x=++tot,S=val;
else S=min(S,val);
if(l==r)return;
int mid=(l+r)>>1;
if(mid>=pos)ins(ls,l,mid,pos,val);
else ins(rs,mid+1,r,pos,val);
}
int ask(int x,int l,int r,int L,int R){
if(!x)return inf;
if(L>=l&&R<=r)return S;
int mid=(L+R)>>1,cur=inf;
if(mid>=l)cur=min(cur,ask(ls,l,r,L,mid));
if(mid<r)cur=min(cur,ask(rs,l,r,mid+1,R));
return cur;
}
inline void add(int l,int r,int val){for(;r<=n;r+=r&-r)ins(rt[r],1,n,l,val);}
void dfs(int x,int l,int r){
if(!x)return;
int mid=(l+r)>>1;
dfs(ls,l,mid);dfs(rs,mid+1,r);
}
inline int qry(int l,int r){
int cur=inf;
for(;r>=l;r-=r&-r)
cur=min(cur,ask(rt[r],l,n,1,n));
return cur;
}
int solve(int l,int r){
if(l>r)return 0;
int cur=qry(l,r);
if(cur==inf)return 0;
return rr[cur]-ll[cur]+1+solve(l,ll[cur]-1)+solve(rr[cur]+1,r);
}
bool cmp(int x,int y){return rr[x]-ll[x]>rr[y]-ll[y];}
int main(){
scanf("%d%d",&n,&m);
rep(i,1,m)scanf("%d%d",&ll[i],&rr[i]),p[i]=i;
sort(p+1,p+m+1,cmp);int j=1;puts("No Copy");
pre(i,n,1){
while(j<=m&&rr[p[j]]-ll[p[j]]+1==i)add(ll[p[j]],rr[p[j]],p[j]),j++;
ans[i]=solve(1,n);
}
rep(i,1,n)printf("%d\n",ans[i]);
return 0;
}