zoukankan      html  css  js  c++  java
  • 二进制分组

    是一种以一个(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;
    }
    
  • 相关阅读:
    select中的简单联动
    php中的魔术方法简介
    Mysql常用知识
    php程序员需要注意的问题
    android webview "Uncaught SecurityError: Failed to read the 'localStorage' property from 'Window': Access is denied for this document.", source: (1)
    gallery调用setselection时有动画的两个方法:
    视频格式资料
    判断网络3,4,2,wifi
    重载的view的ontouchevent不响应的解决办法
    SpannableString用法注意
  • 原文地址:https://www.cnblogs.com/knife-rose/p/13036602.html
Copyright © 2011-2022 走看看