模拟赛20200224【直径期望(积分),本质不同子序列(矩阵乘法优化),强连通分量序列(贪心DP)】

24 篇文章 0 订阅
22 篇文章 0 订阅
T1:Expectation

给出 n n n个点的树,树的边权是 [ 0 , 1 ] [0,1] [0,1]中随机的一个实数,求直径的期望长度。
n ≤ 100 n\le100 n100

题解:

在这里插入图片描述
Freopen的题解

Code:待补。


T2:Sequence

一个长度为 n n n的包含大小写字母的字符串, Q Q Q次询问区间本质不同的子序列个数,强制在线。
n , Q ≤ 1 0 6 n,Q\le10^6 n,Q106

题解:

一般求本质不同的子序列数是设 f [ i ] f[i] f[i]表示前 i i i个字母的序列数:
如果 s [ i ] s[i] s[i]没有出现过, f [ i ] = f [ i − 1 ] + f [ i − 1 ] + 1 f[i]=f[i-1]+f[i-1]+1 f[i]=f[i1]+f[i1]+1;反之,记 x x x为上次出现位置, f [ i ] = f [ i − 1 ] + f [ i − 1 ] − f [ x − 1 ] f[i]=f[i-1]+f[i-1]-f[x-1] f[i]=f[i1]+f[i1]f[x1]
但是这样做与 x x x相关,不好拓展到区间询问。

更改状态为 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个字母中以字符 j j j结尾的本质不同的子序列数。
{ j = s [ i ] ,   f [ i ] [ j ] = 1 + ∑ f [ i − 1 ] [ k ] j ≠ s [ i ] ,   f [ i ] [ j ] = f [ i − 1 ] [ j ] \begin{cases} j=s[i],~f[i][j]=1+\sum f[i-1][k]\\ j\neq s[i],~f[i][j]=f[i-1][j] \end{cases} {j=s[i], f[i][j]=1+f[i1][k]j=s[i], f[i][j]=f[i1][j]
这个转移可以很方便地表示成矩阵形式。就是单位矩阵的第 s [ i ] s[i] s[i]列全部改为1。
没有修改,可以预处理从左到右乘的前缀积矩阵,和从右到左乘的前缀逆矩阵。(从右到左是因为矩阵不一定满足交换律, A n s [ l , r ] = M l − 1 → 1 − 1 ∗ M 1 → r Ans[l,r]=M_{l-1\to 1}^{-1}*M_{1\to r} Ans[l,r]=Ml111M1r
暴力转移是 O ( ∑ 3 ) O(\sum^3) O(3)的,需要挖掘矩阵性质。下面的分析截自LXno_name
在这里插入图片描述
Code:

#include<bits/stdc++.h>
#define maxn 1000005
#define M 52
using namespace std;
const int mod = 998244353;
int n,Q,a,b,p,q,r,ans,A[M+1][M+1],S[maxn][M+1],sum[M+1],tag[M+1],B[maxn][M+1];
char s[maxn];
int main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	scanf("%s%d%d%d%d%d%d",s+1,&Q,&a,&b,&p,&q,&r),n=strlen(s+1);
	for(int i=0;i<=M;i++) sum[i]=A[i][i]=1;
	for(int i=1;i<=n;i++){
		int t=s[i]<='Z'?s[i]-'A':s[i]-'a'+26; long long x;
		for(int j=0;j<=M;j++) x=A[j][t],A[j][t]=sum[j],S[i][j]=sum[j]=(sum[j]+A[j][t]-x+mod)%mod;
	}
	memset(A,0,sizeof A);
	for(int i=0;i<=M;i++) A[i][i]=1;
	for(int i=1;i<=n;i++){
		int t=s[i]<='Z'?s[i]-'A':s[i]-'a'+26;
		for(int j=0;j<=M;j++){
			A[t][j]=(A[t][j]+tag[j])%mod;
			tag[j]=(tag[j]+mod-A[t][j])%mod,A[t][j]=(A[t][j]+mod-tag[j])%mod;
		}
		for(int j=0;j<=M;j++) B[i][j]=(A[M][j]+tag[j])%mod;
	}
	while(Q--){
		int a0=a,b0=b;
		a=(1ll*p*a0+1ll*q*b0+ans+r)%mod;
		b=(1ll*p*b0+1ll*q*a0+ans+r)%mod;
		int l=min(a%n,b%n)+1,r=max(a%n,b%n)+1;
		if(l==1) ans=S[r][M];
		else {ans=0;for(int i=0;i<=M;i++) ans=(ans+1ll*B[l-1][i]*S[r][i])%mod;}
	}
	printf("%d\n",ans);
}

T3:Counting

在这里插入图片描述
V ≤ 100 V\le100 V100

题解:

因为关心的只是强连通分量序列的变化,所以可以尝试贪心地构造一种加边的方法使其可以涵盖所有强连通分量的变化情况。
可以看做从1往外连了一条链,其余的点都是单独的强连通分量,在链上不断缩强连通分量,就可以涵盖所有的情况。下面的分析同样截自LXno_name
在这里插入图片描述
Code:

#include<bits/stdc++.h>
#define maxn 105
using namespace std;
const int mod = 998244353;
int n,f[maxn*maxn][maxn][maxn];
inline int add(int x,int y){return (x+=y)>=mod?x-mod:x;}
inline int dec(int x,int y){return (x-=y)<0?x+mod:x;}
int main()
{
	freopen("counting.in","r",stdin);
	freopen("counting.out","w",stdout);
	scanf("%d",&n);
	f[0][1][1]=1;
	for(int i=0;i<n*(n-1);i++)
		for(int j=1,lim=min(n,i+(i==0));j<=lim;j++)
			for(int k=j,lk=min(n,i+1);k<=lk;k++) if(f[i][j][k]){
				int x=f[i][j][k];
				if(k<n) f[i+1][j][k+1]=add(f[i+1][j][k+1],x);
				else if((k*(k-1)+j*(j-1))/2>i) f[i+1][j][k]=add(f[i+1][j][k],x);
				for(int l=1;l<=k-j;l++) f[i+1][j+l][k]=add(f[i+1][j+l][k],x);
			}
	for(int i=1;i<=n*(n-1);i++){
		int ans=0;
		for(int j=1,lim=min(n,i);j<=lim;j++)
			for(int k=j,lk=min(n,i+1);k<=lk;k++)
				ans=add(ans,f[i][j][k]);
		printf("%d%c",ans,i==n*(n-1)?10:32);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值