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;
    }
    
  • 相关阅读:
    mac os programming
    Rejecting Good Engineers?
    Do Undergrads in MIT Struggle to Obtain Good Grades?
    Go to industry?
    LaTex Tricks
    Convert jupyter notebooks to python files
    How to get gradients with respect to the inputs in pytorch
    Uninstall cuda 9.1 and install cuda 8.0
    How to edit codes on the server which runs jupyter notebook using your pc's bwroser
    Leetcode No.94 Binary Tree Inorder Traversal二叉树中序遍历(c++实现)
  • 原文地址:https://www.cnblogs.com/knife-rose/p/13036602.html
Copyright © 2011-2022 走看看