是一种以一个(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))
字符串匹配通过(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;
}
设询问向量为((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;
}