二进制分组

Posted knife-rose

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二进制分组相关的知识,希望对你有一定的参考价值。

是一种以一个(log)为代价,解决一类强制在线带修改问题的方法

如果不强制在线,我们可以用各种姿势奇奇怪怪的分治做法

基本思想是我们将一个查询前的修改操作按二进制分组,例如某个询问前有(23)个修改操作,那么我们将这些修改操作分为大小为(16,4,2,1)的四组

当新加入一个修改时,我们在右侧加入一个(1)得到(16,4,2,1,1)

当最右侧两个组大小相等时,将其合并为同一组得到(16,4,2,2),最终合并为(16,8)

如果合并两个组的代价是组内元素大小的话,那么修改总复杂度将是(O(nlogn)),每个元素合并一次会花费一个复杂度的代价,显然一个元素最多被合并(log)

最多同时存在(log)组修改操作,假设计算一组修改的复杂度是(O(k)),那么询问操作的复杂度是(O(nklogn))

CF710F

字符串匹配通过(AC)自动机实现,按照上面的方法开(log)(AC)自动机

删除操作可以视为再开一份(AC)自动机,用总答案减去删除的的答案

注意(AC)自动机因为要合并,所以(trie)树和补图要同时维护,每次合并(trie)树后重新建立补图

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
	inline int read()
	{
		int x=0;char ch,f=1;
		for(ch=getchar();(ch<‘0‘||ch>‘9‘)&&ch!=‘-‘;ch=getchar());
		if(ch==‘-‘) f=0,ch=getchar();
		while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();}
		return f?x:-x;
	}
	const int N=3e5+10,p=1e9+9;
	int Q;
	char s[N];
	struct ACM
	{
		int son[N][26],fail[N],num[N],tot[N],vis[N][26];
		int rt[N],top,cnt,siz[N];
		inline int merge(int x,int y)
		{
			if(!x||!y) return x|y;
			tot[x]+=tot[y];
			for(int i=0;i<26;++i) son[x][i]=merge(son[x][i],son[y][i]);
			return x;
		}
		inline void getfail(int rt)
		{
			queue<int> q;
			for(int i=0;i<26;++i)
			{
				if(son[rt][i]) vis[rt][i]=son[rt][i],fail[son[rt][i]]=rt,q.push(vis[rt][i]);
				else vis[rt][i]=rt;
			}
			while(!q.empty())
			{
				int now=q.front();q.pop();
				for(int i=0;i<26;++i)
				{
					if(son[now][i])
					{
						vis[now][i]=son[now][i],fail[son[now][i]]=vis[fail[now]][i];
						q.push(vis[now][i]);
					}
					else vis[now][i]=vis[fail[now]][i];
				}
				num[now]=tot[now]+num[fail[now]];
			}
		}
		inline void insert(char *s)
		{
			rt[++top]=++cnt,siz[top]=1;
			int now=rt[top];
			for(int i=1,len=strlen(s+1);i<=len;++i)
			{
				int c=s[i]-‘a‘;
				if(!son[now][c]) son[now][c]=++cnt;
				now=son[now][c];
			}
			tot[now]=1;
			while(siz[top]==siz[top-1]) --top,rt[top]=merge(rt[top],rt[top+1]),siz[top]+=siz[top+1];
			getfail(rt[top]);
		}
		inline int query(char *s)
		{
			int ret=0,len=strlen(s+1);
			for(int i=1;i<=top;++i)
			{
				int now=rt[i];
				for(int j=1;j<=len;++j)
				{
					now=vis[now][s[j]-‘a‘];
					ret+=num[now];
				}
			}
			return ret;
		}
	}A,B;
	inline void main()
	{
		Q=read();
		for(int opt,i=1;i<=Q;++i)
		{
			opt=read();scanf("%s",s+1);
			if(opt==1) A.insert(s);
			if(opt==2) B.insert(s);
			if(opt==3) printf("%lld
",A.query(s)-B.query(s));
			fflush(stdout);
		}
	}
}
signed main()
{
	red::main();
	return 0;
}

[SDOI2014]向量集

设询问向量为((x_0,y_0)),点积为(x_0*x+y_0*y)

(ans=max{y_0*frac{x_0}{y_0}x+y})

我们假设(y_0ge 0) 否则将所有数字取反 得到

(ans=y_0*max{frac{x_0}{y_0}x+y})

后面这个东西说明答案集合在上凸包上,所以我们要维护凸包

考虑二进制分组,用线段树的结构,每次向最新一个位置插入向量,如果线段树一个区间被填满了,就把这个区间的凸包建立出来

询问的时候在(log)个凸包上三分

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1) 
#define lowbit(i) ((i)&(-i))
	inline int read()
	{
		int x=0;char ch,f=1;
		for(ch=getchar();(ch<‘0‘||ch>‘9‘)&&ch!=‘-‘;ch=getchar());
		if(ch==‘-‘) f=0,ch=getchar();
		while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();}
		return f?x:-x;
	}
	const int N=4e5+10,inf=2e18;
	int n,ans,tot;
	char s[5];
	bool encode;
	inline void decode(int& x){x^=(ans&0x7fffffff);}
	struct node
	{
		int x,y;
		inline node operator + (const node &t) const
		{
			return (node){x+t.x,y+t.y};
		}
		inline node operator - (const node &t) const
		{
			return (node){x-t.x,y-t.y};
		}
		inline int operator * (const node &t) const
		{
			return x*t.x+y*t.y;
		}
		inline int operator ^ (const node &t) const
		{
			return x*t.y-y*t.x;
		}
		inline bool operator < (const node &t) const
		{
			return x<t.x||(x==t.x&&y<t.y);
		}
	};
	int sum[N<<2];
	vector<node> a[2][N<<2];
	node sor[N];
	int num;
	inline void merge(int p,int id)
	{
		int sum1=a[id][ls(p)].size(),sum2=a[id][rs(p)].size();
		int t1=0,t2=0;num=0;
		while(t1<sum1&&t2<sum2)
		{
			if(a[id][ls(p)][t1]<a[id][rs(p)][t2]) sor[++num]=a[id][ls(p)][t1++];
			else sor[++num]=a[id][rs(p)][t2++];
		}
		while(t1<sum1) sor[++num]=a[id][ls(p)][t1++];
		while(t2<sum2) sor[++num]=a[id][rs(p)][t2++];
		int top=0;
		for(int i=1;i<=num;++i)
		{
			while(top>1&&((a[id][p][top-1]-a[id][p][top-2])^(sor[i]-a[id][p][top-2]))>=0) a[id][p].pop_back(),--top;
			a[id][p].push_back(sor[i]);++top;
		}
	}
	inline void update(int pos,int l,int r,int p,int x,int y)
	{
		++sum[p];
		if(l==r)
		{
			a[0][p].push_back((node){x,y});
			a[1][p].push_back((node){-x,-y});
			return;
		}
		if(pos<=mid) update(pos,l,mid,ls(p),x,y);
		else update(pos,mid+1,r,rs(p),x,y);
		if(sum[p]==r-l+1) merge(p,0),merge(p,1);
	}
	inline int query(int tl,int tr,int l,int r,int p,int x,int y,int id)
	{
		if(tl<=l&&r<=tr)
		{
			int l=0,r=a[id][p].size()-1;
			node now=(node){x,y};
			while(r-l>5)
			{
				int mid1=l+(r-l)/3,mid2=r-(r-l)/3;
				if(a[id][p][mid1]*now<=a[id][p][mid2]*now) l=mid1;
				else r=mid2;
			}
			int ret=-inf;
			for(int i=l;i<=r;++i) ret=max(ret,a[id][p][i]*now);
			return ret;
		}
		int ret=-inf;
		if(tl<=mid) ret=max(ret,query(tl,tr,l,mid,ls(p),x,y,id));
		if(tr>mid) ret=max(ret,query(tl,tr,mid+1,r,rs(p),x,y,id));
		return ret;
	}
	inline void main()
	{
		n=read();scanf("%s",s);
		encode=s[0]!=‘E‘;
		for(int x,y,l,r,i=1;i<=n;++i)
		{
			scanf("%s",s);
			x=read(),y=read();
			if(encode) decode(x),decode(y);
			if(s[0]==‘A‘) update(++tot,1,n,1,x,y);
			else
			{
				l=read(),r=read();
				if(encode) decode(l),decode(r);
				if(y>=0) printf("%lld
",ans=query(l,r,1,n,1,x,y,0));
				else printf("%lld
",ans=query(l,r,1,n,1,-x,-y,1));
			}
		}
	}
}
signed main()
{
	//freopen("r1.in","r",stdin);
	red::main();
	return 0;
}

以上是关于二进制分组的主要内容,如果未能解决你的问题,请参考以下文章

片段事务中的实例化错误

android.view.InflateException:二进制 XML 文件第 15 行:二进制 XML 文件第 19 行:膨胀类片段时出错

SQL如何对分组后运算出来的结果进行排序

当我切换到包含片段的活动时应用程序崩溃(二进制 XML 文件第 10 行:二进制 XML 文件第 10 行:膨胀类片段时出错)

动态规划背包问题总结:01完全多重与其二进制优化分组背包 题解与模板

如何从RegEx分组中“排除”空白区域?