zroi day23——杂题详解

洛谷 P3750 [六省联考 2017] 分手是祝愿

思路

首先以考试时的思维来思考,发现 \(n=k\) 至少有 \(50\) 分,于是首先思考部分分。

首先我们先来思考 \(n=k\) 的意义,当 \(n=k\) 时,就代表对于步数的期望一定是最小步数,因为对于每个灯我们最多操作一次,所以所有灯最多操作 \(n\) 次,而最优操作步数在 \(k\) 以内的时候,我们采用最优操作,所以步数的期望一定是最小步数(其实 \(k \le\) 最小操作数时同理)。

考虑到后面的灯一定不会被前面的影响到,所以倒序遍历,如果需要关就关掉,这样一定最优。

已经在省选中仅仅一题就获得了 \(50\) 分的高分了(其实已经有 \(80\) 分了,赢!)。

考虑正解(\(+50\) 分捏),首先当 \(k \le\) 最小操作数时,同上。实际上,要操作的序列是固定的。这里具象化的将操作序列化为一个 \(01\) 序列,\(0\) 表示不需要操作此灯,\(1\) 则表示需要。如 \(10101\) 表示 \(1\)\(3\)\(5\) 位置需要操作。我们随机选择位置操作,当选中了一个 \(0\) 时,就需要多操作一次;当选中了一个 \(1\) 时,就减少了一次操作。考虑到我们目前的状态都和目前的操作数有关,于是设状态 \(f_{i}\) 表示从最少还需 \(i\) 个操作变为最少还需 \(i-1\) 个操作的期望操作数。

有以下式子:

\[\large \begin{aligned} f_{i}=1\times\frac{i}{n} + \frac{n-i}{n}\times(1+f_{i+1}+f_{i}) \end{aligned} \]

这个式子一看就不是很友好。。。把式子拆开吧:

\[\large \begin{aligned} f_{i}=\frac{n+(n-i) \times f_{i+1}}{i} \end{aligned} \]

起始状态是 \(f_{n+1}\),很明显它的值为 \(0\)

我们设整个序列需要操作数为 \(cnt\),那么最后的期望步数即为:

\[\begin{aligned} (\sum_{i=k+1}^{cnt} f_{i})+k \end{aligned} \]

乘上 \(n!\) 即为最终答案。

Code

/*
啊啊哎哎啊啊啊啊啊啊啊爱爱欸哦啊啊爱爱爱爱爱 
pm_pro 2024/8/8 
*/
#include <bits/stdc++.h>
#define upp(a,x,y) for(int a=x;a<=y;a++)
#define dww(a,x,y) for(int a=x;a>=y;a--)
#define int long long
using namespace std;
const int N=1e5+10,X=1e5+3;
int a[N],f[N],n,k,cnt;
int qmi(int a,int b,int p) {
	int res=1;
	while(b) {
		if(b&1) res=res*a%p;
		a=a*a%p;b>>=1;
	}
	return res;
}
signed main() {
	cin>>n>>k;
	upp(i,1,n) cin>>a[i];
	dww(i,n,1)//模拟最优情况
		if(a[i]) {
			cnt++;
			for(int j=1;j<=i/j;j++)
				if(i%j==0) {
					a[j]^=1;
					if(j*j!=i) a[i/j]^=1;
				}
		}
	f[n+1]=0;
	dww(i,n,1) {
		int tmp=(n%X+(n-i)*f[i+1]%X)%X;
		tmp=tmp*qmi(i,X-2,X)%X;
		f[i]=tmp;
	}int res=1,sum=0;
	upp(i,1,n) res=res*i%X;
	if(cnt<=k) cout<<cnt*res%X,exit(0);
	upp(i,k+1,cnt) sum=(sum+f[i])%X;
	sum=(sum+k)%X;sum=sum*res%X;
	cout<<sum;
	return 0;
} 

AT_agc058_b [AGC058B] Adjacent Chmax

思路

在文中,为方便介绍,我们称一次操作为“染色”,而在操作过程中更新了一个元素的值,我们称那个元素“被染色”,一个元素 \(x\) 与另一个元素 \(y\) 操作,使另一个元素更新值,称“用 \(x\) 来染色”。

首先发现如果用 \(a_{i}\) 来染色,最左染到左边第一个比他大的数的右边,最右染到右边第一个比他大的数的左边。

所以我们定义 \([l_{i},r_{i}]\) 表示用一个元素最左、最右能染到的位置,这就是那个元素的影响范围。

考虑 DP,设状态为 \(f_{i,j}\) 表示目前考虑到第 \(i\) 个元素,用这 \(i\) 个元素染色到了 \(j\),显然初始状态 \(f_{0,0}\)\(1\)

讨论方案数哪里来:

考虑讨论 \(j(l_{i} \le j \le r_{i})\) 的染色情况,如果 \(j\) 不染色,那么说明原来就有,从 \(f_{i-1,j}\) 转移而来,如果染色,那么从 \(f_{i,j-1}\) 转移而来,即又往右染了一个元素。

不在上述区间的方案数就直接继承 \(f_{i-1,j}\) 的方案数。

Code

//很有趣的一道题目,代码不长思维很精妙 
#include <bits/stdc++.h>
#define int long long
#define upp(a,x,y) for(int a=x;a<=y;a++)
#define dww(a,x,y) for(int a=x;a>=y;a--)
using namespace std;
const int N=5010,X=998244353;
int f[N][N],a[N],n;
signed main() {
	cin>>n;upp(i,1,n) cin>>a[i],f[0][0]=1;
	upp(i,1,n) {
		int l=i,r=i;
		while(l-1>=1&&a[l-1]<=a[i]) l--;
		while(r+1<=n&&a[r+1]<=a[i]) r++;
		upp(j,0,n) f[i][j]=f[i-1][j];
		upp(j,l,r) f[i][j]=(f[i][j]+f[i][j-1])%X;
	}
	cout<<f[n][n]<<endl;
	return 0;
} 
posted @ 2024-08-08 11:44  PM_pro  阅读(17)  评论(0)    收藏  举报