2020-3-1 模拟测验记录与题解

文章目录

      • 前言
      • 题目
        • T1
        • T2
        • T3
      • 题解
        • T1
        • T2
        • T3
      • 代码
        • T1
        • T2
        • T3

前言

这是一次对我来说非常有技术含量的比赛。感谢教练能找到这套题。

题目

T1

一棵 n n n 个节点的树,有边权和点权。

d i s ( x , y ) dis(x,y) dis(x,y) 定义为 x x x y y y 路径上的边权和。 v x v_x vx 为点 x x x 的点权。

无序点对 ( x , y ) (x,y) (x,y) 的权值定义为 d i s ( x , y ) ∗ ( v x  xor  v y ) dis(x,y) * (v_x \ \text{xor} \ v_y) dis(x,y)(vx xor vy)

q q q 次操作,每次修改一个点的点权,然后询问当前所有不同无序点对的权值和。

n , q ≤ 30000 n,q \leq 30000 n,q30000,边权 [ 1 , 100 ] [1,100] [1,100],点权 [ 0 , 16384 ) [0,16384) [0,16384)

T2

两个长度为 n n n 的字符串 s 1 s1 s1 s 2 s2 s2。保证两者只可能由前 m m m 种小写英文字母组成。( a a a 是第一种, b b b 是第二种,……)并且两字符串 LCS 长度为 n − 1 n-1 n1

给定 s 1 s1 s1,问存在多少种不同的 s 2 s2 s2

n ≤ 1 0 5 n \leq 10^5 n105 m ≤ 26 m \leq 26 m26

T3

一个 n n n 个点的无向完全图,每条边的边权都是 [ 1 , L ] [1,L] [1,L] 内的整数。

问有多少种不同的无向完全图满足 1 1 1 n n n 的最短路长为 k k k

两个图不同当且仅当存在两图中各一条边,它们起点,终点相同但边权不同。

答案对 1 0 9 + 7 10^9 + 7 109+7 取模。

n , k ≤ 12 n,k \leq 12 n,k12 L ≤ 1 0 9 L \leq 10^9 L109

题解

T1

70 分做法:

首先我们要考虑处理出修改前的所有不同无序点对的权值和。

这个东西有一个很自然很套路的分位考虑想法,按二进制位将原树拆成 15 个,每棵树的点权都是 0/1。

0/1 就有一个很 simple 的做法。考虑 dfs,对于每个点存储其为根的子树内 0 的个数,1 的个数,到所有 0 的距离和,到所有 1 的距离和。这样就能 O ( n ) O(n) O(n) 做了。

所以我们可以用 O ( 15 n ) O(15n) O(15n) 预处理。

对于修改,我们考虑只会影响目标点到根这一条链上的各种值,所以我们暴力把当前点在这一段的贡献去掉,然后再暴力重新加上。

总复杂度 O ( 15 n + 15 q ∗ d e p t h ) O(15n + 15q*depth) O(15n+15qdepth)(depth 是树高)

在随机树下有很优秀的运行效率。

100 分做法:

我们考虑原来的预处理很像点分治。于是我们考虑真的用点分治做这道题。

这个的复杂度是 O ( 15 n log ⁡ n + 15 q log ⁡ n ) O(15n\log n + 15q \log n) O(15nlogn+15qlogn)

T2

很有技巧的 DP。

首先考虑怎么判断两串的 LCS 是否长 n − 1 n-1 n1

正常的 LCSdp 策略是 L C S i , j LCS_{i,j} LCSi,j 表示一个串的前 i i i 个字符与另一个串的前 j j j 个字符的 LCS 长度,而这里我们很明显只需要 L C S i , i − 1 LCS_{i,i-1} LCSi,i1 L C S i , i LCS_{i,i} LCSi,i L C S i , i + 1 LCS_{i,i+1} LCSi,i+1。(下文用 L C S i , − 1 LCS_{i,-1} LCSi,1 L C S i , 0 LCS_{i,0} LCSi,0 L C S i , 1 LCS_{i,1} LCSi,1 代替)

容易发现,若要求合法且可能满足最后 LCS 长度为 n − 1 n-1 n1,那么肯定有:

i − 2 ≤ L C S i , − 1 ≤ i − 1 i-2 \leq LCS_{i,-1} \leq i-1 i2LCSi,1i1

0 ≤ L C S i , 0 − L C S i , − 1 ≤ 1 0\leq LCS_{i,0} - LCS_{i,-1} \leq 1 0LCSi,0LCSi,11

0 ≤ L C S i , 1 − L C S i , 0 ≤ 1 0\leq LCS_{i,1} - LCS_{i,0} \leq 1 0LCSi,1LCSi,01

可以发现,数组 { i − 2 , L C S i , − 1 , L C S i , 0 , L C S i , 1 } \{ i-2, LCS_{i,-1}, LCS_{i,0}, LCS_{i,1} \} {i2,LCSi,1,LCSi,0,LCSi,1} 的差分数组将会是一个 01 串。我们可以把这个 01 串作为实际 dp 的一维状态去进行转移。

现在我们定义 d p i , j , k dp_{i,j,k} dpi,j,k 表示:串 2 的前 i i i 个字符和串 1 组成的 LCSdp 差分数组状态为 j j j,且串 2 第 i i i 个字符为 k k k 时,串 2 的种类数。转移时可以直接把原数组还原,跑一遍 LCSdp 确定到达的状态,也可以预建出 LCSdp 状态转移的自动机。

由于我们把某个 dp 压在了另一个 dp 的状态之中,所以这种技巧常称为 dp 套 dp。

这里我采用的是转移时暴力 LCSdp,有一定的常数。合法的差分数组共有 5 个,转移常数在 3 左右。

所以时间复杂度是 O ( 15 n m ) O(15nm) O(15nm)

T3

很好的计数题。

考虑的步骤:想到用 dis 数组和最短路三角不等式 → \rightarrow 想到容斥计算答案 → \rightarrow 想到根据容斥原理把 dis 大于 k k k 的点压缩 → \rightarrow 考虑到 dis 枚举困难,转为枚举 dis 数组对应的桶 → \rightarrow 解决此题

直接上最后一步的结论。

我们令 b u c i buc_i buci 代表与 1 1 1 的最短路为 i i i 的点的个数。其中 0 ≤ i ≤ k + 1 0 \leq i \leq k + 1 0ik+1 i = k + 1 i = k+1 i=k+1 象征了所有 d i s > k dis > k dis>k 的情况。

dfs 枚举这个数组,方案数不会很多。

UPD:关于方案数的计算。其实就是把 n-2 个球塞到 k+1 个筐里,允许空筐的方案数。这个问题很基本,用插板法可以得出答案是 C n + k − 2 k C_{n+k-2}^{k} Cn+k2k 。极限情况下这个数值是 6 ∗ 1 0 5 6 * 10^5 6105 左右。

已知一个确定的 b u c buc buc 数组,我们考虑如何计数。

假设现在枚举到下标 i i i。首先我们要把这个“个数”分配到点上,这个方案就是在剩余可选的点里选择 b u c i buc_i buci 个,是一个组合数。

然后,我们要让这些点满足 d i s = i dis = i dis=i

对于所有 d i s < i dis < i dis<i 的点 x x x,我们要满足所有 d i s x + v ≥ i dis_x + v \geq i disx+vi 且存在 d i s x + v = i dis_x + v = i disx+v=i。这个东西用容斥统计一下即可。

然后对于所有 d i s = i dis = i dis=i 的其他点,与当前点的连边很明显可以从 1 1 1 取到 L L L

最后我们解决 d i s > k dis > k dis>k 的问题。

容易发现,我们如果只要求对于所有 d i s ≤ k dis \leq k disk 的点 x x x 满足 d i s x + v > k dis_x + v > k disx+v>k,并且对于所有 d i s > k dis > k dis>k 的其他点,与当前点的连边可以从 1 1 1 取到 L L L,那么这些点与其连出的边必然能导出一种 > k > k >k 的最短路。而且,如此统计不重不漏。

所以统计的方法相当于我们正常的容斥步骤去掉了“减去”那一步。

至此,我们就解决了确定 b u c buc buc 数组时的计数,答案是上述所有算出的方案相乘。

最后把所有情况相加即可。

总复杂度 O ( C n + k − 2 k × k 2 ) O(C_{n+k-2}^{k} \times k^2) O(Cn+k2k×k2)

代码

T1

写了个动态点分治。

#include 
#include 
using namespace std;
const int N=3e4+1;
int bg[N],nx[N*2],to[N*2],va[N*2],tl;
inline void add(int x,int y,int v)
{
	nx[++tl]=bg[x];
	bg[x]=tl;
	to[tl]=y;
	va[tl]=v;
}
int tv[N],dep[N],val[N],fir[N],lg2[N*2],st[N*2][20],dfn;
inline int dist(int x,int y)
{
	if(x==0||y==0) return 0;
	if(fir[x]>fir[y]) swap(x,y);
	int lg=lg2[fir[y]-fir[x]+1];
	return dep[x]+dep[y]-min(st[fir[x]][lg],st[fir[y]-(1<<lg)+1][lg])*2;
}
void dfs(int now,int f,int depth)
{
	fir[now]=++dfn;
	st[dfn][0]=dep[now]=depth;
	for(int i=bg[now];i;i=nx[i])
	{
		int aim=to[i];
		if(aim==f) continue;
		dfs(aim,now,depth+va[i]);
		st[++dfn][0]=depth;
	}
}
int book[N],siz[N],fa[N],sum[N][15][2],ctr;
long long ans[N][15],sumd[N][15][2],sumfd[N][15][2];
void dfs_prep(int now,int f)
{
	siz[now]=1;
	for(int i=bg[now];i;i=nx[i])
	{
		int aim=to[i];
		if(aim==f||book[aim]) continue;
		dfs_prep(aim,now);
		siz[now]+=siz[aim];
	}
}
void dfs_find(int now,int f,int out)
{
	if(out>(siz[now]+out)/2) return;
	int ok=1;
	for(int i=bg[now];i;i=nx[i])
	{
		int aim=to[i];
		if(aim==f||book[aim]) continue;
		if(siz[aim]>(siz[now]+out)/2) ok=0;
		dfs_find(aim,now,out+siz[now]-siz[aim]);
	}
	if(ok) ctr=now;
}
void build(int now)
{
	book[now]=1;
	for(int i=bg[now];i;i=nx[i])
	{
		int aim=to[i];
		if(book[aim]) continue;
		dfs_prep(aim,now),dfs_find(aim,now,0);
		fa[ctr]=now,build(ctr);
	}
}
int stk[N],top;
void modify(int pos,int v)
{
	int i,j;
	for(i=0;i<15;i++)
	{
		if((v&(1<<i))!=(val[pos]&(1<<i)))
		{
			for(j=pos;j;j=fa[j]) stk[++top]=j;
			int tmp=stk[top--];
			while(top)
			{
				int las=stk[top--];
				ans[tmp][i]-=sumd[tmp][i][0]*2*sum[tmp][i][1]+sumd[tmp][i][1]*2*sum[tmp][i][0];
				ans[tmp][i]+=2*sumfd[las][i][0]*sum[las][i][1]+2*sumfd[las][i][1]*sum[las][i][0]-ans[las][i];
				tmp=las;
			}
			int tdis=dist(pos,fa[pos]),typ=val[pos]&(1<<i)?1:0;
			ans[pos][i]+=2*sumd[pos][i][typ]-2*sumd[pos][i][typ^1];
			sum[pos][i][typ^1]++,sum[pos][i][typ]--;
			sumfd[pos][i][typ^1]+=tdis,sumfd[pos][i][typ]-=tdis;
			tmp=pos;
			for(j=fa[pos];j;j=fa[j])
			{
				sum[j][i][typ^1]++,sum[j][i][typ]--;
				tdis=dist(pos,j);
				sumd[j][i][typ^1]+=tdis,sumd[j][i][typ]-=tdis;
				tdis=dist(pos,fa[j]);
				sumfd[j][i][typ^1]+=tdis,sumfd[j][i][typ]-=tdis;
				ans[j][i]+=sumd[j][i][0]*2*sum[j][i][1]+sumd[j][i][1]*2*sum[j][i][0];
				ans[j][i]-=2*sumfd[tmp][i][0]*sum[tmp][i][1]+2*sumfd[tmp][i][1]*sum[tmp][i][0]-ans[tmp][i];
				tmp=j;
			}
		}
	}
	val[pos]=v;
}
int main()
{
	int n,q,i,j,root;
	scanf("%d",&n);
	for(i=1;i<=n;i++) scanf("%d",&tv[i]);
	for(i=1;i<n;i++)
	{
		int x,y,v;
		scanf("%d%d%d",&x,&y,&v);
		add(x,y,v),add(y,x,v);
	}
	dfs(1,0,0);
	lg2[0]=-1;
	for(i=1;i<=dfn;i++) lg2[i]=lg2[i>>1]+1;
	for(i=1;i<20;i++)
		for(j=1;j<=dfn-(1<<i)+1;j++)
			st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
	dfs_prep(1,0),dfs_find(1,0,0);
	root=ctr,build(ctr);
	for(i=1;i<=n;i++)
	{
		for(j=0;j<15;j++)
		{
			sum[i][j][0]++,sumfd[i][j][0]+=dist(i,fa[i]);
			for(int k=fa[i];k;k=fa[k])
				sum[k][j][0]++,sumd[k][j][0]+=dist(i,k),sumfd[k][j][0]+=dist(i,fa[k]);
		}
	}
	for(i=1;i<=n;i++) modify(i,tv[i]);
	scanf("%d",&q);
	for(i=1;i<=q;i++)
	{
		int x,v;
		scanf("%d%d",&x,&v);
		modify(x,v);
		long long tans=0;
		for(j=0;j<15;j++) tans+=(1<<j)*ans[root][j];
		printf("%lld\n",tans>>1);
	}
	return 0;
}

T2

实际应用时,为防止 MLE 需要滚动数组。

#include 
#include 
using namespace std;
const int N=1e5+5;
long long dp[2][8][26];
char s[N];
int calc(int pos,int st,int tmp)
{
	int dp1,dp2,dp3,ret1,ret2,ret3;
	dp1=(st&4)?pos-1:pos-2;
	dp2=(st&2)?dp1+1:dp1;
	dp3=(st&1)?dp2+1:dp2;
	ret1=max(dp1+(s[pos]==tmp+'a'),dp2);
	ret2=max(dp2+(s[pos+1]==tmp+'a'),max(ret1,dp3));
	ret3=max(dp3+(s[pos+2]==tmp+'a'),ret2);
	return (ret1-pos+1)*4+(ret2-ret1)*2+(ret3-ret2);
}
int main()
{
	int n,m,i,j,k;
	scanf("%d%d%s",&n,&m,s+1);
	if(n==1)
	{
		printf("%d",m-1);
		return 0;
	}
	dp[1][6][s[1]-'a']=1;
	if(s[1]!=s[2]) dp[1][5][s[2]-'a']=1;
	for(i=0;i<m;i++) if(i!=s[1]-'a'&&i!=s[2]-'a') dp[1][4][i]=1;
	for(i=1;i<n;i++)
	{
		int tmp=i&1;
		for(j=2;j<=6;j++)
		{
			long long sum=0;
			for(k=0;k<m;k++)
			{
				sum+=dp[tmp][j][k];
				dp[tmp][j][k]=0;
			}
			for(k=0;k<m;k++)
			{
				int nxt=calc(i,j,k);
				dp[tmp^1][nxt][k]+=sum;
			}
		}
	}
	long long ans=0;
	for(i=2;i<=5;i++)
		for(j=0;j<m;j++)
			ans+=dp[n&1][i][j];
	printf("%lld",ans);
	return 0;
}

T3

#include 
#include 
using namespace std;
const int N=15,MOD=1e9+7;
int n,k,l,disc[N],ans;
int C[N][N];
inline int qpow(int base,int expo)
{
	int ret=1;
	while(expo)
	{
		if(expo&1) ret=1ll*ret*base%MOD;
		base=1ll*base*base%MOD;
		expo>>=1;
	}
	return ret;
}
int solve()
{
	int i,j,tmp=1;
	for(i=1;i<=k;i++)
	{
		if(!disc[i]) continue;
		int add=1,dec=1;
		for(j=0;j<i;j++)
		{
			add=1ll*add*qpow(max(0,l-i+j+1),disc[j])%MOD;
			dec=1ll*dec*qpow(max(0,l-i+j),disc[j])%MOD;
		}
		int fn=(0ll+add-dec+MOD)%MOD;
		tmp=1ll*tmp*qpow(fn,disc[i])%MOD*qpow(l,disc[i]*(disc[i]-1)/2)%MOD;
	}
	for(i=0;i<=k;i++) tmp=1ll*tmp*qpow(max(0,l-k+i),disc[i]*disc[k+1])%MOD;
	tmp=1ll*tmp*qpow(l,disc[k+1]*(disc[k+1]-1)/2)%MOD;
	return tmp;
}
void dfs(int now,int rem,int cnt)
{
	if(now==k+1)
	{
		disc[k+1]=rem;
		int ctb=1ll*solve()*cnt%MOD;
		ans=(0ll+ans+ctb)%MOD;
		return;
	}
	for(int i=0;i<=rem;i++)
	{
		disc[now]+=i;
		dfs(now+1,rem-i,1ll*cnt*C[rem][i]%MOD);
		disc[now]-=i;
	}
}
int main()
{
	int i,j;
	scanf("%d%d%d",&n,&k,&l);
	C[0][0]=1;
	for(i=1;i<=n;i++)
	{
		for(j=0;j<=i;j++)
		{
			if(j==0) C[i][j]=1;
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
		}
	}
	disc[0]=1,disc[k]=1;
	dfs(1,n-2,1);
	printf("%d",ans);
	return 0;
}

你可能感兴趣的:(OI)