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\) 个操作的期望操作数。
有以下式子:
这个式子一看就不是很友好。。。把式子拆开吧:
起始状态是 \(f_{n+1}\),很明显它的值为 \(0\)。
我们设整个序列需要操作数为 \(cnt\),那么最后的期望步数即为:
乘上 \(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;
}

浙公网安备 33010602011771号