冲刺国赛7.18
T1
先分类讨论把 \(\max\) 拆开
假设 \(a_c+b_c\geq a_y+b_y\)
\(a_c-a_y\geq b_y-b_c\)
于是对于每个蛋糕都记录一个判据量 \(h\)
属于 \(L\) 的判据为 \(c-y\) 属于 \(C\) 的是 \(y-c\)
当 \(a\) 的判据大于等于 \(b\) 的时候取最小的 \(b_c\)
再开一个关于 \(h\) 值域的线段树,叶子节点用 \(\text{multiset}\) 维护最小的 \(c\) 和 \(y\)
合并时根据判据的大小分别从左右儿子中取 \(c\) 和 \(y\)
在叶子节点时,两个判据相当也可以取最小值
Code
#include<bits/stdc++.h>
#define int long long
#define lson rt<<1
#define rson rt<<1|1
#define inf 0x3f3f3f3f3f3f3f3f
#define meow(args...) fprintf(stderr,args)
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
bool mem1;
int n,rt,tot;
int O[2000010],cnt;
int op[1000010],d[1000010],c[1000010],y[1000010],pos[1000010];
struct seg{int mnc[2],mny[2],ans;}st[1000010*4];
multiset<int>s[1000010][2][2];//pos l/c c/y
inline void pushup(int rt){
st[rt].ans=min(st[lson].ans,st[rson].ans);
st[rt].ans=min(st[rt].ans,st[lson].mnc[1]+st[rson].mnc[0]);
st[rt].ans=min(st[rt].ans,st[lson].mny[0]+st[rson].mny[1]);
st[rt].mnc[0]=min(st[lson].mnc[0],st[rson].mnc[0]);
st[rt].mny[0]=min(st[lson].mny[0],st[rson].mny[0]);
st[rt].mnc[1]=min(st[lson].mnc[1],st[rson].mnc[1]);
st[rt].mny[1]=min(st[lson].mny[1],st[rson].mny[1]);
}
void upd(int rt,int l,int r,int pos,int i){
if(l==r){
if(op[i]==1){
s[l][d[i]][0].insert(c[i]);st[rt].mnc[d[i]]=*s[l][d[i]][0].begin();
s[l][d[i]][1].insert(y[i]);st[rt].mny[d[i]]=*s[l][d[i]][1].begin();
}
if(op[i]==0){
s[l][d[i]][0].erase(s[l][d[i]][0].find(c[i]));if(!s[l][d[i]][0].empty()) st[rt].mnc[d[i]]=*s[l][d[i]][0].begin();else st[rt].mnc[d[i]]=inf;
s[l][d[i]][1].erase(s[l][d[i]][1].find(y[i]));if(!s[l][d[i]][1].empty()) st[rt].mny[d[i]]=*s[l][d[i]][1].begin();else st[rt].mny[d[i]]=inf;
}
st[rt].ans=min(st[rt].mnc[0]+st[rt].mnc[1],st[rt].mny[0]+st[rt].mny[1]);
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) upd(lson,l,mid,pos,i);
else upd(rson,mid+1,r,pos,i);
pushup(rt);
}
bool mem2;
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
freopen("cake.in","r",stdin);
freopen("cake.out","w",stdout);
n=read();memset(st,0x3f,sizeof(st));
for(int i=1;i<=n;i++){
op[i]=read(),d[i]=read(),c[i]=read(),y[i]=read();
if(d[i]==0) O[++cnt]=pos[i]=c[i]-y[i];
if(d[i]==1) O[++cnt]=pos[i]=y[i]-c[i];
}
sort(O+1,O+1+cnt);cnt=unique(O+1,O+1+cnt)-O-1;
for(int i=1;i<=n;i++) pos[i]=lower_bound(O+1,O+1+cnt,pos[i])-O;
for(int i=1;i<=n;i++){
upd(1,1,cnt,pos[i],i);
printf("%lld\n",st[1].ans<=2000000000?st[1].ans:-1ll);
}
return 0;
}
T2
一行一行的转移,设 \(f_i=[i\in S]\)
发现如果 \(b\) 只有一种转移
那就是普通的位运算卷积
现在有很多种 \(b\) 发现每一位和每一位之间是不影响的
所以可以按位考虑,根据当前位的 \(b\) 去做 \(\text{fwt}\)
然后行与行之间的转移都是相同的,于是再做个快速幂
Code
#include<bits/stdc++.h>
#define int long long
#define i2 500000004
#define mod 1000000007
#define inf 0x3f3f3f3f3f3f3f3f
#define meow(args...) fprintf(stderr,args)
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,m,q,len;
int a[1048576],b[1048576];
char s[1048576];
inline int qpow(int x,int k){
int res=1,base=x;
while(k){if(k&1) res=res*base%mod;base=base*base%mod;k>>=1;}
return res;
}
inline void fwt(int tx){
for(int d=1,bit=0;d<len;d<<=1,bit++) for(int i=0;i<len;i+=d<<1) for(int j=0;j<d;j++){
int x=a[i+j],y=a[i+j+d];
if(b[bit]==1) (a[i+j+d]+=a[i+j]*tx)%=mod;
if(b[bit]==2) (a[i+j]+=a[i+j+d]*tx)%=mod;
if(b[bit]==3){
a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;
if(tx==mod-1) a[i+j]=a[i+j]*i2%mod,a[i+j+d]=a[i+j+d]*i2%mod;
}
}
}
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
freopen("completion.in","r",stdin);
freopen("completion.out","w",stdout);
n=read(),m=read();scanf("%s",s);len=1<<m;
for(int i=0;i<m;i++) b[i]=read();
for(int i=0;i<len;i++) a[i]=s[i]-'0';
fwt(1);
for(int i=0;i<len;i++) a[i]=qpow(a[i],n);
fwt(mod-1);
q=read();
for(int i=1;i<=q;i++) printf("%lld\n",a[read()]);
return 0;
}
T3
找两个前缀的最长后缀,可以用 \(\text{SAM}\)
直接找他们两个前缀代表的节点在 \(\text{parent}\) 树上的 \(\text{LCA}\) 就是他们的最长后缀
现在要找的就是两两之间的最长前缀,可以把这些节点代表的串建出 \(\text{Trie}\)
然后最长前缀的长度就是 \(\text{LCA}\) 的深度,然后就变成了一个经典问题,可以用树剖做
现在瓶颈在于建 \(\text{Trie}\)
发现这颗 \(\text{Trie}\) 的节点很少,且和 \(\text{SAM}\) 上的节点一一对应
于是直接枚举 \(\text{SAM}\) 上的每条边,如果两个节点代表的长度正好差 \(1\) 就说明是 \(\text{Trie}\) 上的边
Code
#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f3f3f3f3f
#define meow(args...) fprintf(stderr,args)
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,q,tot=1,lst=1,ans;
int pos[500010];
char s[500010];
bool vis[1000010];
namespace Trie{
int c1[1000010],c2[1000010];
int fa[1000010],son[1000010],dep[1000010],siz[1000010],top[1000010],dfn[1000010],clo;
int head[1000010],ver[1000010],to[1000010],cnt;
inline void add(int x,int y){
ver[++cnt]=y;to[cnt]=head[x];head[x]=cnt;
}
inline void ins(int l,int r,int k){
for(int w=(l-1)*k;l<=tot;l+=l&-l) c1[l]+=k,c2[l]+=w;r++;
for(int w=(r-1)*k;r<=tot;r+=r&-r) c1[r]-=k,c2[r]-=w;
}
inline int query(int l,int r){
int res=0;
for(int w=r;r;r-=r&-r) res+=c1[r]*w-c2[r];l--;
for(int w=l;l;l-=l&-l) res-=c1[l]*w-c2[l];
return res;
}
void dfs1(int x,int fath,int depth){
fa[x]=fath,dep[x]=depth,siz[x]=1;
int maxson=-1;
for(int i=head[x];i;i=to[i]){
int y=ver[i];dfs1(y,x,depth+1);
siz[x]+=siz[y];
if(siz[y]>maxson) maxson=siz[y],son[x]=y;
}
}
void dfs2(int x,int topf){
dfn[x]=++clo;
top[x]=topf;if(!son[x]) return;
dfs2(son[x],topf);
for(int i=head[x];i;i=to[i]){
int y=ver[i];if(y==son[x]) continue;
dfs2(y,y);
}
}
inline void upd(int x,int k){
while(top[x]!=1) ins(dfn[top[x]],dfn[x],k),x=fa[top[x]];
if(dfn[x]>1) ins(dfn[1]+1,dfn[x],k);
}
inline int query(int x){
int res=0;
while(top[x]!=1) res+=query(dfn[top[x]],dfn[x]),x=fa[top[x]];
res+=query(dfn[1],dfn[x]);
return res;
}
}
namespace SAM{
struct node{int len,fa,son[4];}t[1000010];
int fa[1000010],son[1000010],dep[1000010],siz[1000010],top[1000010];
int head[1000010],ver[1000010],to[1000010],cnt;
inline void add(int x,int y){
ver[++cnt]=y;to[cnt]=head[x];head[x]=cnt;
}
inline void extend(int c,int u){
int p=++tot,f=lst;lst=p;
t[p].len=t[f].len+1;pos[u]=p;
while(f&&!t[f].son[c]) t[f].son[c]=p,f=t[f].fa;
if(!f) return t[p].fa=1,void();
int x=t[f].son[c],y=++tot;
if(t[f].len+1==t[x].len) return tot--,t[p].fa=x,void();
t[y]=t[x];t[y].len=t[f].len+1,t[x].fa=t[p].fa=y;
while(f&&t[f].son[c]==x) t[f].son[c]=y,f=t[f].fa;
}
void dfs1(int x,int fath,int depth){
fa[x]=fath,dep[x]=depth,siz[x]=1;
int maxson=-1;
for(int i=head[x];i;i=to[i]){
int y=ver[i];dfs1(y,x,depth+1);
siz[x]+=siz[y];
if(siz[y]>maxson) maxson=siz[y],son[x]=y;
}
}
void dfs2(int x,int topf){
top[x]=topf;if(!son[x]) return;
dfs2(son[x],topf);
for(int i=head[x];i;i=to[i]){
int y=ver[i];if(y==son[x]) continue;
dfs2(y,y);
}
}
inline int LCA(int x,int y){
while(top[x]!=top[y]) (dep[top[x]]>dep[top[y]])?x=fa[top[x]]:y=fa[top[y]];
return (dep[x]<dep[y])?x:y;
}
void build(){
for(int i=2;i<=tot;i++) add(t[i].fa,i);
for(int i=1;i<=tot;i++) for(int j=0;j<4;j++) if(t[i].son[j]) if(t[i].len+1==t[t[i].son[j]].len) Trie::add(i,t[i].son[j]);
dfs1(1,0,1);dfs2(1,1);Trie::dfs1(1,0,1);Trie::dfs2(1,1);
// for(int i=1;i<=tot;i++) printf("t[%lld].len : %lld\n",i,t[i].len);
// for(int i=1;i<=tot;i++) for(int j=0;j<4;j++) if(t[i].son[j]) printf("%lld -> %lld\n",i,t[i].son[j]);
// for(int i=1;i<=n;i++) printf("pos[%lld] : %lld\n",i,pos[i]);
}
}
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
freopen("elixir.in","r",stdin);
freopen("elixir.out","w",stdout);
scanf("%s",s+1);n=strlen(s+1);
for(int i=1;i<=n;i++) SAM::extend(s[i]-'a',i);SAM::build();
q=read();
for(int i=1,x,y,z;i<=q;i++){
x=pos[read()],y=pos[read()];
z=SAM::LCA(x,y);
if(vis[z]){
vis[z]=0,Trie::upd(z,-1),ans-=Trie::query(z);
}else{
vis[z]=1,ans+=Trie::query(z),Trie::upd(z, 1);
}
printf("%lld\n",ans);
}
return 0;
}