通常,我们的分治是递归地写的,这样好写,比如常见的 cdq 分治写法:
void solveFunc(int l,int r) {
//解决跨区间问题
}
void divideFunc(int l,int r) {
if(l==r) return ; //递归边界
int mid=(l+r)/2;
divideFunc(l,mid); //处理左半问题
divideFunc(mid+1,r); //处理右半问题
solveFunc(l,r); //处理跨区间问题
}
不同的分治在 divideFunc
内的顺序不完全相同,而 solveFunc
则是完全针对题目设计的。
可以知道,若解决的问题长度是 divideFunc
的递归深度是
比如,若 solveFunc
的复杂度是
通常以上写法已经足够用了。但是假设维护的数据结构有特殊性,这样递归写,复杂度就爆了。
递归查询点的顺序不是有序的,比如序列:
一种解决方法是回滚,就是同时维护加入和删除,以达到想要的区间。这样做,我们得先把
假如数据结构支持的查询是和前缀有关的(比如前缀前
struct datatype{};
struct bfs_element {
int l,r;
datatype data;
};
datatype solveFunc(int l,int r) {}
void bfsDivideFunc(int n) {
std::queue<bfs_element> Q;
Q.push({1,n,data});
while(!Q.empty()) {
bfs_element now=Q.front(); Q.pop();
datatype newdata=solveFunc(now.l,now.r);
if(now.l==now.r) continue;
int mid=(l+r)>>1;
Q.push({1,mid,newdata});
Q.push({mid+1,r,newdata});
}
}
刚才的遍历顺序变为:
那么,我们的数据结构就只需要加入这一个操作。加入遍历完一遍,就直接把数据结构清空或销毁。重新开始即可。复杂度可靠。
给定两个长度为
的序列 。定义 为 到 中前 小的数的和。对于每个 ,求 。
首先发现一个性质,就是若
有人说,可以用主席树做。没问题。但是这个题也可以用权值线段树做。先将权值线段树离散化(或者动态开点)。然后利用刚才提到的“拉平的 bfs”求解。实现如下:
#define int long long
#define R myio::read_int()
const int kS=1e5+5,kRANGE=1000;
struct node {
int sum,cnt;
node() {sum=cnt=0;}
node(int _sum,int _cnt)
:sum(_sum),cnt(_cnt) {}
void clear() {sum=cnt=0;}
};
int n,s[kS],a[kS],ans[kS];
class SegmentTree {
private:
node* nodes;
int range;
int end;
void PUSH_UP(int p) {
nodes[p].sum=nodes[p<<1].sum+nodes[p<<1|1].sum;
nodes[p].cnt=nodes[p<<1].cnt+nodes[p<<1|1].cnt;
}
void INSERT(int X,int l,int r,int p) {
if(l>=r) {nodes[p].sum+=l,nodes[p].cnt++; return ;}
int mid=(l+r)>>1;
if(mid>=X) INSERT(X,l,mid,p<<1);
else INSERT(X,mid+1,r,p<<1|1);
PUSH_UP(p);
}
int QUERY(int C,int l,int r,int p) {
if(l==r) return l*C;
int mid=(l+r)>>1;
if(C>nodes[p<<1].cnt) return nodes[p<<1].sum
+QUERY(C-nodes[p<<1].cnt,mid+1,r,p<<1|1);
return QUERY(C,l,mid,p<<1);
}
void CLEAR() {for(int i=0;i<=range*4;i++) nodes[i].clear(); end=0;}
public:
SegmentTree(int _range) {
range=_range; end=0;
nodes=new node[range*4+1];
}
void insert(int x) {
if(x<end) CLEAR();
for(int i=end+1;i<=x;i++) INSERT(a[i],1,range,1);
end=x;
}
int query(int x) {return QUERY(x,1,range,1);}
~SegmentTree() {delete []nodes;}
};
struct ques {
int l,r,pl,pr;
ques(int _l,int _r,int _pl,int _pr)
:l(_l),r(_r),pl(_pl),pr(_pr) {}
};
std::queue<ques> Q;
void tMin(int &x,int y,int &fx,int fy) {if(x>y) x=y,fx=fy;}
signed main() {
n=R; SegmentTree *segtree=new SegmentTree(kRANGE);
for(int i=1;i<=n;i++) s[i]=R;
for(int i=1;i<=n;i++) a[i]=R;
Q.push(ques(1,n,1,n));
int nl,nr,npl,npr,mid,res,resp;
for(int i=1;i<=n;i++) {
ques now=Q.front(); Q.pop();
nl=now.l,nr=now.r,npl=now.pl,npr=now.pr;
mid=(nl+nr)>>1,res=9e18,resp=0;
for(int j=std::max(npl,mid);j<=npr;j++)
segtree->insert(j),tMin(res,segtree->query(mid)+s[j],resp,j);
ans[mid]=res;
if(mid>nl) Q.push(ques(nl,mid-1,npl,resp));
if(mid<nr) Q.push(ques(mid+1,nr,resp,npr));
}
myio::print_int(ans+1,ans+1+n,'\n');
delete segtree;
return 0;
}