zr2019暑期高端峰会AB组day9

zr2019暑期高端峰会AB组day9

A. 快乐链覆盖

link

  • 这题是个大力树D的,考场上感觉不用输出答案的部分分都不是很好写,就放弃了,后来老师竟然说这部分是个普及组的树D。。。。(我觉得细节很多啊)
  • 我们设 f [ u ] [ j ] [ 0 / 1 / 2 ] f[u][j][0/1/2] f[u][j][0/1/2]代表u子树中有j条链,目前u处的状态为0/1/2,0代表u处没被任何链覆盖,1代表u被一条链覆盖并且正出于链的一端,2代表u在一条链的中间,链的两端都在子树里
  • 耐心地写写转移方程,这部分大概就可以了
  • 关于输出方案,大概都是这个套路,但是好像在这题里它就特别码农,每次转移记录一下从什么状态转移过来,从上到下搜一遍,还原方案

过于码农,有待填坑。。。

B. 快乐线段树

link

  • 这道题60分是白给的,想打100分要经过细致的推式子,要有耐心而且不能出错,出错很难调,我赛场上没能推出式子,但是赛后听完讲,推了一波式子一发就码A了
  • 60分做法,对于每个询问区间,我们对于每个值域和它有交的线段树节点分别计算它被遍历到了几次,那么假设我们对于节点 [ l , r ] [l,r] [l,r],计算询问 [ L , R ] [L,R] [L,R]经过它的次数,那么这部分的贡献就是 [L,R]的子区间总数 - [L,R]中与[l,r]无交的区间数 - [L,R]中包含[l,r]父区间的区间数
  • 那么60分的做法时间错在哪里,我们对比它和正常线段树的复杂度,唯一一处比正常线段树更慢的地方在于,当走到一个节点被询问区间完全包含时,我们继续搜了下去,而正常线段树 O ( 1 ) O(1) O(1)就返回了答案,所以我们考虑在这种节点上想办法 O ( 1 ) O(1) O(1)返回整棵子树的贡献
  • 那么对于每个这样的节点它的 [L,R]中与[l,r]无交的区间数 以及[L,R]中包含 [l,r]父区间的区间数 都是关于L和R的多项式,它的系数和询问区间无关,之和节点有关,而这些节点是不变的,我们就先预处理好这些节点上的系数,用的时候就能做到 O ( 1 ) O(1) O(1)
  • 推系数的过程较为复杂,不再赘述

C. 快乐KMP

link

  • 首先,部分分做法。我会识字! 20 20 20分。我会 K M P KMP KMP 40 40 40分。我会前缀和!(我会nm的) 60分
  • 思考 f [ i ] f[i] f[i]的实际意义,前缀 i i i b o r d e r border border我们就 f [ i ] + + f[i]++ f[i]++,并且跳到最长 b o r d e r border border那里继续,实际上就是前缀 i i i b o r d e r border border的总个数
  • 思考 G ( A ) G(A) G(A)的意义, f [ i ] f[i] f[i]求和那么就是每个前缀在大串中出现的次数减一
  • 我们看 H ( B ) H(B) H(B)要枚举子串不太好搞,把 H ( B ) H(B) H(B)差分一下,设为 P ( B ) P(B) P(B),那么继续思考 P ( B ) P(B) P(B)的意义,其实就是 G ( B ) G(B) G(B)又求了个前缀和,那就是 ∑ i = 1 n ( k i 2 ) \sum_{i=1}^n \tbinom{k_i}{2} i=1n(2ki),其中 k i k_i ki代表前缀 i i i在大串中出现的次数
  • 继续思考,我们要维护和某个串在大串中出现次数有关的东西,我会 S A M SAM SAM!那么一个节点上的所有串的次数就是,子树中 n p np np类节点的个数, S A M SAM SAM基操
  • 还没做完呢,由于刚才我们差分过了,于是求答案时我们只需动态加入一个字符并求得当前的 P ( B ) P(B) P(B),和上一位的答案相加即可,而 P ( B ) P(B) P(B)又是 G ( B ) G(B) G(B)的后缀和,所以我们求 P ( B ) P(B) P(B)只需将上一轮的 P ( B ) P(B) P(B)加上这一轮的 G ( B ) G(B) G(B),那么 L C T LCT LCT或者树剖维护 G ( B ) G(B) G(B),这题就莫得了,老师的方法不是维护 G G G,而是维护 P P P这个二次函数,更加麻烦一点点
  • 再看看具体怎么维护的细节,当我们在末尾加上一个字符时,对于它在 p a r e n t parent parent树上到根的这条链,都是它的前缀,它们出现的次数都加一,那么总共出现的次数的增量就时这条链上所表示的子串数。那么初值怎么赋呢?由于我们的G时出现次数减一,那么我们也可以把这个减一看作时新加入的那个,我们先查询再插入即可
  • 最后一点, S A M SAM SAM有关数组要开二倍
T2赛后代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register ll i=(a);i<=(b);i++)
#define For(i,a,b) for (register ll i=(a);i>=(b);i--)
#define mem(i,j) memset(i,j,sizeof(i))
using namespace std;
typedef long long ll;
const ll N=1e5+5;
const ll mod=1e9+7;
const ll inv=500000004;
ll n,q,tot,opt1,opt2;
ll ans[N],zuo[N<<2],you[N<<2],siz[N<<2];
ll c_LL,c_RR,c_LR,c_L,c_R,c_C,Ans;
ll LL_1[N<<2],RR_1[N<<2],L_1[N<<2],R_1[N<<2],C_1[N<<2];
ll LL_11[N<<2],RR_11[N<<2],L_11[N<<2],R_11[N<<2],C_11[N<<2];
ll LR_2[N<<2],L_2[N<<2],R_2[N<<2],C_2[N<<2];
ll LR_22[N<<2],L_22[N<<2],R_22[N<<2],C_22[N<<2];
inline ll read()
{
	ll x=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return f*x;
}
inline void Getval(ll p,ll l,ll r)
{
	if (l==r)
	{
		LL_1[p]=inv;
		RR_1[p]=inv;
		L_1[p]=(mod-inv*(2*l+1)%mod)%mod;
		R_1[p]=inv*(mod+1-2*r%mod)%mod;
		C_1[p]=(l*l%mod+l+r*r%mod-r+mod)%mod*inv%mod;
		return;
	}
	LL_1[p]=inv;
	RR_1[p]=inv;
	L_1[p]=(mod-inv*(2*l+1)%mod)%mod;
	R_1[p]=inv*(mod+1-2*r%mod)%mod;
	C_1[p]=(l*l%mod+l+r*r%mod-r+mod)%mod*inv%mod;
	LR_2[p]=mod-2;
	L_2[p]=2*(r-1)%mod;
	R_2[p]=2*(l+1)%mod;
	C_2[p]=2*(mod-l*r%mod+l-r+1)%mod;
	return;
}
inline void merge(ll p,ll ls,ll rs)
{
	LL_11[p]=(LL_11[ls]+LL_11[rs]+LL_1[ls]+LL_1[rs])%mod;
	RR_11[p]=(RR_11[ls]+RR_11[rs]+RR_1[ls]+RR_1[rs])%mod;
	L_11[p]=(L_11[ls]+L_11[rs]+L_1[ls]+L_1[rs])%mod;
	R_11[p]=(R_11[ls]+R_11[rs]+R_1[ls]+R_1[rs])%mod;
	C_11[p]=(C_11[ls]+C_11[rs]+C_1[ls]+C_1[rs])%mod;
	LR_22[p]=(LR_22[ls]+LR_22[rs]+LR_2[p])%mod;
	L_22[p]=(L_22[ls]+L_22[rs]+L_2[p])%mod;
	R_22[p]=(R_22[ls]+R_22[rs]+R_2[p])%mod;
	C_22[p]=(C_22[ls]+C_22[rs]+C_2[p])%mod;
	return;
}
inline void Build(ll p,ll l,ll r)
{
	Getval(p,l,r);
	zuo[p]=l,you[p]=r;
	if (l==r)
	{
		siz[p]=1;
		return;
	}
	ll mid=(l+r)>>1,ls=p<<1,rs=p<<1|1;
	Build(ls,l,mid);
	Build(rs,mid+1,r);
	siz[p]=siz[ls]+siz[rs]+1;
	merge(p,ls,rs);
	return;
}
inline ll cal(ll l,ll r)//[l,r]有多少子区间
{
	ll ret=0,tmp=r-l+1;
	ret=(tmp*(tmp-1)/2%mod+tmp)%mod;
	return ret;
}
inline ll cal1(ll l,ll r,ll L,ll R)//当[L,R]中有多少包含[l,r]的区间 
{
	ll ret=0;
	ret=(R-r+1)*(l-L+1)%mod;
	return ret;
}
inline ll cal2(ll l,ll r,ll L,ll R)//计算[L,R]中有多少不与[l,r]相交的区间
{
	ll ret=0,tmp;
	if (L>r||R<l) return cal(L,R);
	if (R>r) ret=(ret+cal(r+1,R))%mod;
	if (L<l) ret=(ret+cal(L,l-1))%mod;
	return ret;
} 
inline ll Query(ll p,ll L,ll R)
{
	ll ret=tot;
	ll mid=(zuo[p]+you[p])>>1,ls=p<<1,rs=p<<1|1,fa=p>>1;
	ret=(ret-cal2(zuo[p],you[p],L,R)+mod)%mod;
	if (L<=zuo[p]&&you[p]<=R)
	{
		c_LL=(c_LL+LL_11[p])%mod;
		c_RR=(c_RR+RR_11[p])%mod;
		c_LR=(c_LR+LR_22[p])%mod;
		c_L=(c_L+L_11[p]+L_22[p])%mod;
		c_R=(c_R+R_11[p]+R_22[p])%mod;
		c_C=(c_C+C_11[p]+C_22[p])%mod;
		ret=(ret+(siz[p]-1)*tot%mod)%mod;
		return ret;
	}
	if (zuo[p]!=you[p]&&L<=mid) ret=(ret+Query(ls,L,R))%mod;
	if (zuo[p]!=you[p]&&R>mid) ret=(ret+Query(rs,L,R))%mod;
	return ret;
}
int main()
{
//	freopen("data.in","r",stdin);
//	freopen("1.out","w",stdout);
	n=read(),q=read();
	Build(1,1,n);
	while (q--)
	{
		c_LL=c_RR=c_LR=c_L=c_R=c_C=Ans=0;
		opt1=read(),opt2=read();
		tot=cal(opt1,opt2);
		Ans=Query(1,opt1,opt2);
	//	printf("Ans = %lld\n",Ans);
	//	printf("LL:%lld RR:%lld LR:%lld L:%lld R:%lld cs:%lld\n",c_LL,c_RR,c_LR,c_L,c_R,c_C);
		Ans=(Ans+mod-c_LL*opt1%mod*opt1%mod)%mod;
		Ans=(Ans+mod-c_RR*opt2%mod*opt2%mod)%mod;
		Ans=(Ans+mod-c_LR*opt1%mod*opt2%mod)%mod;
		Ans=(Ans+mod-c_L*opt1%mod)%mod;
		Ans=(Ans+mod-c_R*opt2%mod)%mod;
		Ans=(Ans+mod-c_C)%mod;
		ans[++ans[0]]=Ans;
	//	printf("%lld\n\n",Ans);
	}
	FOR(i,1,ans[0]) printf("%lld\n",ans[i]);
	return 0;
}
T3赛后代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
#define GO(u) for (register int j=f[u];j!=-1;j=nxt[j])
#define mem(i,j) memset(i,j,sizeof(i))
using namespace std;
typedef long long ll;
const int N=2e5+5;
const int mod=1e9+7;
int n,now;
ll H=0,P=0,ans[N];
char s[N];
int tot=1,last=1,fa[N],ch[N][26],len[N];
int dfnn=0,dfn[N],siz[N],son[N],pre[N],top[N],dep[N],back[N];
ll val[N<<2],sum[N<<2],tag[N<<2];
int cnt=0,f[N],nxt[N<<1];
struct E
{
	int u,v;
}e[N<<1];
void Add(int c)
{
	int np=++tot,p=last;
	last=np,len[np]=len[p]+1;
	while (p&&!ch[p][c]) ch[p][c]=np,p=fa[p];
	if (!p) fa[np]=1;
	else
	{
		int q=ch[p][c];
		if (len[q]==len[p]+1) fa[np]=q;
		else
		{
			int nq=++tot;
			fa[nq]=fa[q];
			FOR(i,0,25) ch[nq][i]=ch[q][i];
			len[nq]=len[p]+1;
			fa[np]=fa[q]=nq;
			while (p&&ch[p][c]==q) ch[p][c]=nq,p=fa[p];
		}
	}
	return;
}
void add(int u,int v)
{
	cnt++;
	nxt[cnt]=f[u];
	f[u]=cnt;
	e[cnt]=(E){u,v};
	return;
}
void DFS1(int u,int pr)
{
	siz[u]=1;
	GO(u)
	{
		int v=e[j].v;
		if (v==pr) continue;
		pre[v]=u;
		dep[v]=dep[u]+1;
		DFS1(v,u);
		siz[u]+=siz[v];
		if ((!son[u])||(siz[son[u]]<siz[v])) son[u]=v;
	}
	return;
}
void DFS2(int u,int t)
{
	top[u]=t;
	dfn[u]=++dfnn;
	back[dfnn]=u;
	if (!son[u]) return;
	DFS2(son[u],t);
	GO(u)
	{
		int v=e[j].v;
		if (v==pre[u]||v==son[u]) continue;
		DFS2(v,v);
	}
	return;
}
void up(int p)
{
	int ls=p<<1,rs=p<<1|1;
	sum[p]=(sum[ls]+sum[rs])%mod;
	return;
}
void bd(int p,int l,int r)
{
	if (l==r)
	{
		val[p]=len[back[l]]-len[fa[back[l]]];
		sum[p]=0;
		return;
	}
	int mid=(l+r)>>1,ls=p<<1,rs=p<<1|1;
	bd(ls,l,mid);
	bd(rs,mid+1,r);
	val[p]=(val[ls]+val[rs])%mod;
	return;
}
void down(int p,int l,int r)
{
	if (!tag[p]) return;
	int mid=(l+r)>>1,ls=p<<1,rs=p<<1|1;
	tag[ls]=(tag[ls]+tag[p])%mod;
	tag[rs]=(tag[rs]+tag[p])%mod;
	sum[ls]=(sum[ls]+tag[p]*val[ls]%mod)%mod;
	sum[rs]=(sum[rs]+tag[p]*val[rs]%mod)%mod;
	tag[p]=0;
	return;
}
void mdf(int p,int l,int r,int L,int R,int v)
{
	if (L<=l&&r<=R)
	{
		tag[p]++;
		sum[p]=(sum[p]+val[p])%mod; 
		return;
	}
	int mid=(l+r)>>1,ls=p<<1,rs=p<<1|1;
	down(p,l,r);
	if (L<=mid) mdf(ls,l,mid,L,R,v);
	if (R>mid) mdf(rs,mid+1,r,L,R,v);
	up(p);
	return;
}
ll qry(int p,int l,int r,int L,int R)
{
	if (L<=l&&r<=R) return sum[p];
	int mid=(l+r)>>1,ls=p<<1,rs=p<<1|1;
	down(p,l,r);
	ll ret=0;
	if (L<=mid) ret=(ret+qry(ls,l,mid,L,R))%mod;
	if (R>mid) ret=(ret+qry(rs,mid+1,r,L,R))%mod;
	up(p);
	return ret;
}
void Mdf(int u)
{
	while (top[u]!=top[1])
	{
		mdf(1,1,tot,dfn[top[u]],dfn[u],1);
		u=fa[top[u]];
	}
	mdf(1,1,tot,dfn[1],dfn[u],1);
	return;
}
ll Qry(int u)
{
	ll ret=0;
	while (top[u]!=top[1])
	{
		ret=(ret+qry(1,1,tot,dfn[top[u]],dfn[u]))%mod;
		u=fa[top[u]];
	}
	ret=(ret+qry(1,1,tot,dfn[1],dfn[u]))%mod;
	return ret;
}
int main()
{
	mem(f,-1);
	scanf("%d",&n);
	scanf("%s",s+1);
	FOR(i,1,n) Add(s[i]-'a');
	FOR(i,1,tot) if (fa[i]) add(fa[i],i),add(i,fa[i]);
	DFS1(1,0),DFS2(1,1),bd(1,1,tot);
	now=1;
	FOR(i,1,n)
	{
		now=ch[now][s[i]-'a'];
		P=(P+Qry(now))%mod;
		Mdf(now);	
		H=(H+P)%mod;
		ans[++ans[0]]=H;
	}
	FOR(i,1,n) printf("%lld\n",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值