题意
题解
代码
#include
using namespace std;
#define int long long
const int N=1e5+10;
int n,m,a[N];
int res,cnt[N];//记录答案个数,记录区间内含有i的数量cnt[i]
signed main() {
cin.tie(0)->sync_with_stdio(false);
cin>>n>>m;
for(int i=0;i<n;i++) cin>>a[i];
int kinds=0,j=0;//区间内含有的数的种类,左指针j
for(int i=0;i<n;i++) {
if(!cnt[a[i]]) kinds++;
cnt[a[i]]++;
if(kinds==m) {//区间内恰好有1~m
while(j<i && cnt[a[j]]>1) res+=n-i,cnt[a[j]]--,j++;//移动左端点
res+=n-i,cnt[a[j]]--,j++,kinds--;//移除左端点,准备找新的区间
}
}
cout<<res<<'\n';
return 0;
}
题意
题解
两只青蛙是独立的,所以只需先算一只青蛙的概率,即先计算i步到n的概率,答案为下式
∑
i
=
1
n
−
1
p
∗
p
\sum_{i=1}^{n-1}p*p
i=1∑n−1p∗p
由题易得,每一种情况的概率都与之前的选择相关,所以为概率dp
定义:f[i,j]表示走i步到达j号桩子的概率
转移:对于f[i,j],j号桩子可以到达的所有桩子概率都乘以1/a[j]
即f[i+1][j+1,j+2...j+a[j]]+=f[i][j]*(1/a[j])
状态转移复杂度为n3,所以要优化。枚举状态需要n2无法改变了,而状态转移这一维可以优化,原先为区间操作O(n),可以利用差分优化成O(1)。即把嵌套在第二层循环里的第三层循环,提到第二重循环外面,与第二重循环并列
代码
#include
using namespace std;
typedef long long LL;
const int N=8010,mod=998244353;
int n,a[N],inv[N];
int d[N][N],f[N][N];//差分,状态概率数组
int qmi(int a,int b) {
int ans=1;
while(b) {
if(b&1) ans=(LL)ans*a%mod;
b>>=1;
a=(LL)a*a%mod;
}
return ans;
}
int main() {
cin.tie(0)->sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],inv[i]=qmi(a[i],mod-2);//逆元预处理好,小优化
f[0][1]=1;//0步到1号桩子概率为100%
for(int i=0;i<n;i++) {//枚举步数,最多n-1步跳到n桩子
for(int j=1;j<=n;j++) {//前缀和计算出f[i][j],因为转移要用
d[i][j]=(d[i][j]+d[i][j-1])%mod;
f[i][j]=(f[i][j]+d[i][j])%mod;
}
for(int j=i;j<n;j++) {//转移,i步最少到了i号桩子;差分处理区间转移
int x=(LL)f[i][j]*inv[j] % mod;
d[i+1][j+1]=(d[i+1][j+1]+x)%mod;
d[i+1][j+1+a[j]]=(d[i+1][j+1+a[j]]-x+mod) % mod;
}
}
LL res=0;
for(int i=1;i<n;i++) res=(res+(LL)f[i][n]*f[i][n]%mod) % mod;
cout<<res<<'\n';
return 0;
}
题意
题解
#include
#include
using namespace std;
int main() {
int t;cin>>t;
while(t--) {
int m;cin>>m;
int len=30;
while(!(m>>len & 1)) len--;//分块数量=2进制位数
vector<int> add(len,0);
int cnt=0;//新增的总共数量
for(int i=len-1;i>=0;i--)//从大到小找插入的位置和数量
if(m>>i & 1) {//为1的位置需要插入
add[i]=len-i-cnt;//插入的数量
cnt+=add[i];
}
printf("%d\n",2*len+cnt+1);//长度为基本的len块,每块有2个,以及插入的cnt个,还有末尾插入的一个
int base=2*len;//插入的数值
for(int i=0;i<len;i++) {
while(add[i]--) printf("%d ",++base);//输出添加插入的数
printf("%d %d ",2*i+2,2*i+1);//输出块
}
printf("%d\n",++base);
}
return 0;
}
题意
题解
因为每多分一段,都与前一段相关,所以dp,朴素版:O(n^3)
定义:f[i,j]表示前j个数分成i段的最小代价
转移:枚举段数i和终点j,转移起点计算答案,代码如下
for(int i=2;i=1;k--) {
f[i][j]=min(f[i][j],f[i-1][k-1]+mx(a[k,k+1,...j]))
}
}
}
单调栈优化
f
[
i
,
j
]
=
m
i
n
(
f
[
i
,
j
]
,
f
[
i
−
1
]
[
k
]
+
m
a
x
(
a
k
+
1
,
a
k
+
2
,
.
.
.
a
j
)
)
f[i,j]=min(f[i,j],f[i-1][k]+max(a_{k+1},a_{k+2},...a_j))
f[i,j]=min(f[i,j],f[i−1][k]+max(ak+1,ak+2,...aj))
滚动数组优化,同时i=1时特殊处理,因为没法i-1
代码
#include
#include
#include
using namespace std;
const int N = 8010, INF = 1e9;
int n, stk[N], top, a[N];
int f[2][N];
int minf[N], minall[N];
int main() {
cin >> n;
for(int i = 1 ; i <= n ; i ++ ) cin >> a[i];
for(int i = 1 ; i <= n ; i ++ ) f[1][i] = max(f[1][i - 1], a[i]);
cout << f[1][n] << endl;
for(int i = 2 ; i <= n ; i ++ ) {
top = 0; minall[0] = INF;
for(int j = i ; j <= n ; j ++ ) {
int temp = f[(i - 1) & 1][j - 1]; // 添加的数为a[j],代价之一为f[i-1][j-1]
while(top && a[stk[top]] <= a[j]) temp = min(temp, minf[top -- ]); // 合并区间
stk[++ top] = j; // 新区间
minf[top] = temp; // 更新新区间的最小f[i - 1][k] (k在区间范围内)
minall[top] = min(minall[top - 1], minf[top] + a[j]); // 更新最小前缀
f[i & 1][j] = minall[top];
}
cout << f[i & 1][n] << endl;
}
return 0;
}
题意
题解
*
代码