题解:P16698 [CSPro 29] 施肥
lailai0916 · · 题解
题意简述
给定
解题思路
两个被覆盖区间首尾相邻时,中间也没有未施肥的田地。因此,已经覆盖到
考虑固定的
使用分治统计答案。对于当前区间
先处理左半部分。对每个
按照
用线段树维护单点加入和区间最大值。右半部分完全对称。按照
接下来计算跨过分界线的信息。对左端点
对右端点
于是跨越分界线的
任何合法覆盖都必须从两侧到达分界线,故两个不等式必要。再证充分性。
现在得到两类点
每个合法区间会在两个端点第一次落入分治两侧时被统计,因而不会遗漏或重复。每层分治扫描当前区间和相关输入区间,并进行对数复杂度的数据结构操作。总时间复杂度为
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=200005;
const int inf=0x3f3f3f3f;
struct Tree
{
int mn[N*4],mx[N*4];
bool tag[N*4];
void reset(int p)
{
mn[p]=inf;
mx[p]=-inf;
tag[p]=1;
}
void init()
{
reset(1);
}
void down(int p)
{
if(!tag[p])return;
reset(p*2);
reset(p*2+1);
tag[p]=0;
}
void add(int p,int l,int r,int x,int y)
{
if(l==r)
{
mn[p]=min(mn[p],y);
mx[p]=max(mx[p],y);
return;
}
down(p);
int mid=(l+r)/2;
if(x<=mid)add(p*2,l,mid,x,y);
else add(p*2+1,mid+1,r,x,y);
mn[p]=min(mn[p*2],mn[p*2+1]);
mx[p]=max(mx[p*2],mx[p*2+1]);
}
int ask_min(int p,int l,int r,int x,int y)
{
if(x<=l&&r<=y)return mn[p];
down(p);
int mid=(l+r)/2,res=inf;
if(x<=mid)res=min(res,ask_min(p*2,l,mid,x,y));
if(y>mid)res=min(res,ask_min(p*2+1,mid+1,r,x,y));
return res;
}
int ask_max(int p,int l,int r,int x,int y)
{
if(x<=l&&r<=y)return mx[p];
down(p);
int mid=(l+r)/2,res=-inf;
if(x<=mid)res=max(res,ask_max(p*2,l,mid,x,y));
if(y>mid)res=max(res,ask_max(p*2+1,mid+1,r,x,y));
return res;
}
void clear(int p,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
{
reset(p);
return;
}
down(p);
int mid=(l+r)/2;
if(x<=mid)clear(p*2,l,mid,x,y);
if(y>mid)clear(p*2+1,mid+1,r,x,y);
mn[p]=min(mn[p*2],mn[p*2+1]);
mx[p]=max(mx[p*2],mx[p*2+1]);
}
}reach_tree,left_tree,right_tree;
int n;
int a[N],b[N],tr[N];
vector<int> ls[N],rs[N];
void add(int x,int y)
{
for(int i=x;i<=n;i+=i&-i)tr[i]+=y;
}
int ask(int x)
{
int res=0;
for(int i=x;i;i-=i&-i)res+=tr[i];
return res;
}
ll solve(int l,int r)
{
if(l==r)return 0;
int mid=(l+r)/2;
ll ans=solve(l,mid)+solve(mid+1,r);
for(int i=mid;i>=l;i--)
{
a[i]=-inf;
b[i]=inf;
for(auto x:ls[i])
{
if(x>r)continue;
if(x<=mid)a[i]=max(a[i],x);
else b[i]=min(b[i],x);
if(x>=mid)right_tree.add(1,1,n,x,i);
}
if(a[i]>-inf)
{
int x=reach_tree.ask_max(1,1,n,i,min(mid,a[i]+1));
a[i]=max(a[i],x);
reach_tree.add(1,1,n,i,a[i]);
}
}
for(int i=mid+1;i<=r;i++)
{
a[i]=inf;
b[i]=-inf;
for(auto x:rs[i])
{
if(x<l)continue;
if(x>mid)a[i]=min(a[i],x);
else b[i]=max(b[i],x);
if(x<=mid+1)left_tree.add(1,1,n,x,i);
}
if(a[i]<inf)
{
int x=reach_tree.ask_min(1,1,n,max(mid+1,a[i]-1),i);
a[i]=min(a[i],x);
reach_tree.add(1,1,n,i,a[i]);
}
}
vector<array<int,3>> event;
for(int i=mid;i>=l;i--)
{
if(a[i]>-inf)
{
int x=left_tree.ask_min(1,1,n,i,a[i]+1);
b[i]=min(b[i],x);
}
if(b[i]<=r)event.push_back({i,0,b[i]});
}
for(int i=mid+1;i<=r;i++)
{
if(a[i]<inf)
{
int x=right_tree.ask_max(1,1,n,a[i]-1,i);
b[i]=max(b[i],x);
}
if(b[i]>=l)event.push_back({b[i],1,i});
}
sort(event.begin(),event.end());
for(auto [x,t,y]:event)
{
if(t==0)add(y,1);
else ans+=ask(y);
}
for(auto [x,t,y]:event)if(t==0)add(y,-1);
reach_tree.clear(1,1,n,l,r);
left_tree.clear(1,1,n,l,r);
right_tree.clear(1,1,n,l,r);
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int l,r;
cin>>l>>r;
ls[l].push_back(r);
rs[r].push_back(l);
}
reach_tree.init();
left_tree.init();
right_tree.init();
cout<<solve(1,n)<<'\n';
return 0;
}