2022杭电多校第九场题解

这篇博客探讨了三个信息技术问题的解决方案:1. 使用动态规划解决套娃分组问题;2. 通过容斥原理计算GCD图中最短路径;3. 模拟计算数组操作的期望结果。文章详细分析了每个问题的思路和AC代码,涉及动态规划、数论和概率计算。
摘要由CSDN通过智能技术生成

2022杭电多校第九场

在这里插入图片描述

Matryoshka Doll(DP)

题意
n n n个套娃,第 i i i个套娃的大小为 a i a_i ai。求将这 n n n个套娃分成 k k k组,且每组排序后相邻两个套娃大小之差不小于 r r r的方案数。

分析
看数据范围可以猜到是 n 2 n^2 n2的DP,用 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个数分为 j j j组的方案数。下面考虑状态转移,假设当前状态为 f [ i ] [ j ] f[i][j] f[i][j],有两种情况:(1) a [ i ] a[i] a[i]单独作为一组,从 f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i1][j1]转移;(2) a [ i ] a[i] a[i]和前面某个数一组,两个数之差大于等于 r r r,从 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j]转移。对于第二种情况,需要预处理能和 a [ i ] a[i] a[i]放在一组的元素的数量。

AC代码

typedef long long ll;
const int mod=998244353;
const int N=5010;
int a[N],L[N],f[N][N]; //L[i]:a[i]左边有多少个数不能和a[i]一组

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n,k,r;
		cin>>n>>k>>r;
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=n;i++)
		{
			L[i]=0;
			for(int j=i-1;j>=1;j--)
			{
				if(abs(a[i]-a[j])<r) L[i]++;
			}
		}
		//初始化,在本题不是必须的
		for(int i=0;i<=n;i++)
		{
			for(int j=0;j<=n;j++)
			{
				f[i][j]=0;
			}
		}
		f[0][0]=1;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=i;j++)
			{
				f[i][j]=(ll)(f[i-1][j-1]+(ll)f[i-1][j]*(j-L[i])%mod)%mod;
			}
		}
		cout<<f[n][k]<<endl;
	}
	return 0;
}

Shortest Path in GCD Graph(容斥原理)

题意
给定含有 n n n个点的完全图,边权为两个点的 g c d gcd gcd。有 q q q次询问,对于每次询问,求两点之间的最短路的长度以及最短路的条数。

分析
如果两个点 x x x y y y g c d gcd gcd为1,从 x x x直接到 y y y路径最短,最短路的长度为1,条数为1。如果 g c d gcd gcd不为1,可以先让 x x x走到与 x x x y y y都互质的点,再走到 y y y,最短路长度不超过2,直接从 x x x y y y的路径长度大于等于2,因此最短路径长度为2。对于 x x x y y y不互质的情况,有两种走法, x x x走到和两个数互质的点,再走到 y y y,或者 x x x直接走到 y y y,此时有 g c d ( x , y ) = 2 gcd(x,y)=2 gcd(x,y)=2。对于第二种情况特判计科,对于第一种情况,可以用容斥原理,因为两个数含有的不同质因数最多只有12个。时间复杂度 O ( q ∗ 2 k ) O(q*2^k) O(q2k)( k k k是两个数含有的不同质因数的个数)。

AC代码

typedef long long ll;
const int N=1e7+10;
int vis[N],prime[N];
int a[105];
int n,q,ans,cnt,k;

void dfs(int x,int sum,int c)
{
	if(x==cnt+1)
	{
		if(c&1) ans-=n/sum;
		else ans+=n/sum;
		return ;
	}
	dfs(x+1,sum,c);
	if((ll)sum*a[x]<=n) dfs(x+1,sum*a[x],c+1);
}

void get_primes(int n)
{
	for(int i=2;i<=n;i++) {
		if(vis[i]==0) { vis[i]=i; prime[++k]=i; }
		for(int j=1;j<=k;j++) {
			if(prime[j]>vis[i]||prime[j]>n/i) break;
			vis[i*prime[j]]=prime[j];
		}
	}
}

int main()
{
	cin>>n>>q;
	get_primes(10000000);
	while(q--)
	{
		int x,y;
		cin>>x>>y;
		if(__gcd(x,y)==1)
		{
			cout<<1<<" "<<1<<endl;
		}
		else
		{
			bool flag=false;
			if(__gcd(x,y)==2) flag=true;
			set<int> s;
			while(x>1)
			{
				s.insert(vis[x]);
				int z=vis[x];
				while(x%z==0) x/=z;
			}
			while(y>1)
			{
				s.insert(vis[y]);
				int z=vis[y];
				while(y%z==0) y/=z;
			}
			cnt=0;
			for(auto x:s) a[++cnt]=x;
			ans=0;
			dfs(1,1,0);
			if(flag) ans++;
			cout<<2<<" "<<ans<<endl;
		}
	}
	return 0;
}

Sum Plus Product(思维)

题意
n n n个数,每次随机取出两个数 a a a b b b,再放入 a ∗ b + a + b a*b+a+b ab+a+b,直到只剩下一个数,求最终得到的数的期望。

分析
最终得到的数和选择顺序无关,直接模拟即可。时间复杂度 O ( n ) O(n) O(n)

AC代码

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		cin>>n;
		vector<ll> v(n);
		for(int i=0;i<n;i++) cin>>v[i];
		while(v.size()>=2)
		{
			ll x=v.back();
			v.pop_back();
			ll y=v.back();
			v.pop_back();
			v.push_back((x+y+x*y%mod)%mod);
		}
		cout<<v[0]<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值