冲刺国赛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;
}
posted @ 2022-07-24 09:51  Max_QAQ  阅读(74)  评论(0编辑  收藏  举报