二进制分组——强制在线的有力算法
Posted llcsblog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二进制分组——强制在线的有力算法相关的知识,希望对你有一定的参考价值。
二进制分组——强制在线的有力算法
这个标题似乎有点既视感
这个算法是在2013年的集训队论文集中《浅谈数据结构题的几个非经典解法》里面介绍的。
给个link,有兴趣的可以自己学习一下。
应用
专门对付强制在线的算法,当修改之间对答案的贡献互相独立(这个和CDQ一样)(或可以快速合并)时,就可以通过套上一个二进制分组的log来做到在线。
介绍
比如现在我有一个数据结构题:
1.添加一个操作:把[l,r]区间中的数加上x。
2.询问如果我执行了[l,r]区间的操作,pos这一个点的值会变为多少。
离线做法就直接大力线段树就可以了,问题不大,相信大家都会。
那如果强制在线呢?
我们一想可以二进制分组(不然我讲它干什么)。
比如当前有22个操作,22=16+4+2
我们对前16个操作建立一个线段树,后面四个建一个,再后面两个建一个。
有二进制进位时就重构就可以了。
实际运用的时候不用真的建好几颗线段树。
重构就判断要不要pushup就可以了。
具体的如果右儿子进位(塞爆)了,自己就也重构就可以了。
例题:UOJ46
/*
@Date : 2020-01-14 22:21:04
@Author : Adscn (adscn@qq.com)
@Link : https://www.cnblogs.com/LLCSBlog
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IL inline
#define RG register
#define gi geti<int>()
#define gl geti<ll>()
#define gc getchar()
#define File(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
template<typename T>IL bool chkmax(T &x,const T &y){return x<y?x=y,1:0;}
template<typename T>IL bool chkmin(T &x,const T &y){return x>y?x=y,1:0;}
template<typename T>
IL T geti()
{
RG T xi=0;
RG char ch=gc;
bool f=0;
while(!isdigit(ch))ch=='-'?f=1:f,ch=gc;
while(isdigit(ch))xi=xi*10+ch-48,ch=gc;
return f?-xi:xi;
}
template<typename T>
IL void pi(T k,char ch=0)
{
if(k<0)k=-k,putchar('-');
if(k>=10)pi(k/10);
putchar(k%10+'0');
if(ch)putchar(ch);
}
const int N=1e5+7;
int n,m;
struct node{
int l,r,a,b;
node(){}
node(int L){l=L;r=a=b=0;}
node(int L,int R,int A,int B){l=L,r=R,a=A,b=B;}
bool operator < (const node &b)const{return l<b.l;}
}t[N<<2];
inline void merge(int &a,int &b,int x,int y)
{
a=1ll*a*x%m,b=(1ll*b*x+y)%m;
}
node operator + (node a,node b){
merge(a.a,a.b,b.a,b.b);
return a;
}
#define mid ((l+r)>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
vector<node>s[N<<2];
inline void pushup(int rt)
{
s[rt].clear();
int len1=s[ls].size(),len2=s[rs].size(),lst=0;
for(int i=0,j=0;i<len1&&j<len2;)
{
int a=s[ls][i].a,b=s[ls][i].b;
merge(a,b,s[rs][j].a,s[rs][j].b);
if(s[ls][i].r<=s[rs][j].r){
s[rt].emplace_back(lst+1,s[ls][i].r,a,b);
lst=s[ls][i].r;
if(s[ls][i].r==s[rs][j].r)++j;
++i;
}
else{
s[rt].emplace_back(lst+1,s[rs][j].r,a,b);
lst=s[rs][j].r;
++j;
}
}
}
inline bool modify(int rt,int l,int r,int pos,node a)
{
if(l==r){
if(a.l!=1)s[rt].emplace_back(1,a.l-1,1,0);
s[rt].push_back(a);
if(a.r!=n)s[rt].emplace_back(a.r+1,n,1,0);
return 1;
}
if(pos<=mid)return modify(ls,l,mid,pos,a),0;
else{
if(modify(rs,mid+1,r,pos,a))pushup(rt);
return 1;
}
}
inline node query(int rt,int l,int r,int L,int R,int pos)
{
// cout<<rt<<" "<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
// if(l>r)return (node){0,0,1,0};
if(L<=l&&r<=R){
// for(auto i:s[rt])printf("Rt=%d:%d %d %d %d
",rt,i.l,i.r,i.a,i.b);
auto ret=*--upper_bound(s[rt].begin(),s[rt].end(),node(pos));
// pi(pos,'
');
// printf("ret :%d %d %d %d
",ret.l,ret.r,ret.a,ret.b);
return ret;
}
if(R<=mid)return query(ls,l,mid,L,R,pos);
if(L>mid)return query(rs,mid+1,r,L,R,pos);
return query(ls,l,mid,L,R,pos)+query(rs,mid+1,r,L,R,pos);
}
int a[N];
int main(void)
{
#ifndef ONLINE_JUDGE
// File("");
#endif
int type=gi;
n=gi,m=gi;
for(int i=1;i<=n;++i)a[i]=gi;
int q=gi,ans=0,cnt=0;
while(q--){
int opt=gi,l=gi,r=gi;
if(type&1)l^=ans,r^=ans;
if(opt==1){
int a=gi,b=gi;
modify(1,1,100000,++cnt,node(l,r,a,b));
}
else{
int pos=gi;
if(type&1)pos^=ans;
auto ret=query(1,1,100000,l,r,pos);
// cout<<ret.a<<" "<<ret.b<<endl;
pi(ans=(1ll*a[pos]*ret.a+ret.b)%m,'
');
}
}
return 0;
}
以上是关于二进制分组——强制在线的有力算法的主要内容,如果未能解决你的问题,请参考以下文章
有人可以解释啥是 SVN 平分算法吗?理论上和通过代码片段[重复]