最近跟着这个大佬 nimphy 学了一下启发式分治
根据目前对启发式分治的理解写一下总结
启发式分治,一般用于解决区间问题,对满足某种条件的区间进行分治,在枚举其分治点的时候进行一些启发式操作
例题一
Non-boring sequences
题目大意:
对一个序列,如果其任意子区间都有至少一个数只出现一次,那么则称这个序列为non-boring的,否则为boring
思路:
预处理每个数上一次出现的位置以及下一次出现的位置
对一个区间,如果有一个数满足条件,则以这个数为分界点,对两边进行分治
在枚举满足条件的分界点时从区间两边向中间进行枚举
使本身可能成为On的算法优化到Onlogn
大致证明可由 T(n) = 2 * T(n/2) + O(n) 得到
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int l[maxn],r[maxn];
int a[maxn];
unordered_map<int,int> m;
bool solve(int L,int R){
if(L>=R) return true;
int ll=L,rr=R;
for(int i=1;i<=R-L+1;i++){
if(i&1){
if(l[ll]<L && r[ll]>R)
return solve(L,ll-1) && solve(ll+1,R);
ll++;
}
else{
if(l[rr]<L && r[rr]>R)
return solve(L,rr-1) && solve(rr+1,R);
rr--;
}
}
return false;
}
int main(){
int T;
cin>>T;
while(T--){
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
m.clear();
for(int i=1;i<=n;i++){
l[i]=m[a[i]];
m[a[i]]=i;
}
m.clear();
for(int i=n;i>=1;i--){
if(m[a[i]]==0) r[i]=n+1;
else r[i]=m[a[i]];
m[a[i]]=i;
}
if(solve(1,n)) cout<<"non-boring"<<endl;
else cout<<"boring"<<endl;
}
}
例题二
hdu6701 杭电多校第10场1011 Make Rounddog Happy
题目大意:
给一个序列a,找到满足条件的所有子区间数量
条件为 max(al, al+1, … , ar) − (r − l + 1) ≤ k 且 区间内每个数字只出现一次
思路:
对区间的最大值进行分治,对于一个区间,区间左端点为L右端点为R,首先找到区间最大值的位置pos作为分界点
对较小的那一段进行枚举,作为结果区间的一个端点,通过预处理O1找到满足条件的区间另一端点的范围
然后对分界点两端的区间继续进行分治
因为每次枚举的都是较小的一段,可保证复杂度仍在nlogn级别内
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
int a[maxn],A[maxn],B[maxn],cnt[maxn];
long long ans=0;
int n,k;
class segment_tree{public:
#define nd node[now]
#define ndl node[now*2]
#define ndr node[now*2+1]
struct segment_node{
int l,r,maxx;
}node[maxn*4];
void pushup(int now){
nd.maxx=max(ndl.maxx,ndr.maxx);
}
void maketree(int s,int t,int now=1){
nd=(segment_node){s,t,0};
if(s==t){
nd.maxx=a[s];
return;
}
maketree(s,(s+t)/2,now*2);
maketree((s+t)/2+1,t,now*2+1);
pushup(now);
}
int query(int s,int t,int now=1){
if(s<=nd.l && t>=nd.r){
return nd.maxx;
}
int maxx=0;
if(s<=ndl.r) maxx=max(maxx,query(s,t,now*2));
if(t>=ndr.l) maxx=max(maxx,query(s,t,now*2+1));
return maxx;
}
}tree;
int getpos(int L,int R){
int l=L,r=R,maxx=tree.query(L,R),pos;
while(l<r){
int mid=(l+r)/2;
if(tree.query(L,mid)<maxx)
l=mid+1;
else
pos=r,r=mid;
}
if(tree.query(L,l)>=maxx) pos=r;
return pos;
}
void solve(int L,int R){
if(L>R) return ;
int pos=getpos(L,R);
if(pos-L<=R-pos){
for(int i=L;i<=pos;i++){
int rr=min(B[i],R);
int ll=max(pos,a[pos]-k+i-1);
if(rr<ll) continue;
ans+=rr-ll+1;
}
}
else{
for(int i=R;i>=pos;i--){
int rr=min(pos,i+1+k-a[pos]);
int ll=max(L,A[i]);
if(rr<ll) continue;
ans+=rr-ll+1;
}
}
solve(L,pos-1);
solve(pos+1,R);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--){
cin>>n>>k;
ans=0;
for(int i=1;i<=n;i++) A[i]=1,B[i]=n;
for(int i=1;i<=n;i++) cin>>a[i];
tree.maketree(1,n);
int l=1,r=n;
for(int i=1;i<=n;i++) cnt[i]=0;
for(int i=1;i<=n;i++){
while(cnt[a[i]]>0){
cnt[a[l]]--;
l++;
}
cnt[a[i]]++;
A[i]=l;
}
for(int i=1;i<=n;i++) cnt[i]=0;
for(int i=n;i>=1;i--){
while(cnt[a[i]]>0){
cnt[a[r]]--;
r--;
}
cnt[a[i]]++;
B[i]=r;
}
solve(1,n);
cout<<ans<<endl;
}
}