CSP-202109

这篇博客探讨了数组操作的两个问题,包括根据前缀最大值数组求原数组元素和的最大值与最小值,以及在自然数数组中找到最大非零段数目的策略。此外,还介绍了概率计算问题,讨论了在不同抽卡概率下获得所有卡的期望次数。这些问题涉及到了数组处理、区间修改和查询以及动态规划等算法思想。
摘要由CSDN通过智能技术生成

数组推导

题意:

自然数数组 A [ 1... n ] A[1...n] A[1...n]。数组 B [ 1... n ] B[1...n] B[1...n]是数组 A A A的前缀最大值数组。给定 B B B数组,求数组 A A A中所有元素和的最大值与最小值

解析:

B [ i ] ≠ B [ i − 1 ] B[i] \neq B[i-1] B[i]=B[i1]时, A [ i ] = B [ i ] A[i] = B[i] A[i]=B[i],此时 A [ i ] A[i] A[i]的值是确定的。
对于确定的 A [ i ] , A [ j ] ( i < j ) A[i],A[j] (i<j) A[i],A[j](i<j) 0 ≤ A [ k ] ≤ A [ i ] ( i < k < j ) 0 \le A[k] \le A[i] (i<k<j) 0A[k]A[i](i<k<j)
因此,当 A [ k ] = 0 A[k] = 0 A[k]=0时, s u m sum sum有最小值;当 A [ k ] = A [ i ] A[k] = A[i] A[k]=A[i]时, s u m sum sum有最大值。时间复杂度: O ( n ) O(n) O(n)

代码:
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 105;
int n, summin, summax;
int a[maxn], b[maxn];
int main(){
	cin >> n;
	for(int i = 1; i <= n; i++)
		cin >> b[i];
	for(int i = 1; i <= n; i++){
		if(b[i] != b[i-1]){
			a[i] = b[i];
			summin += a[i]; 
		}
	}
	for(int i = 2; i <= n; i++){
		if(!a[i])
			a[i] = a[i-1];
	}
	for(int i = 1; i <= n; i++)
		summax += a[i];
	cout << summax << endl;
	cout << summin << endl;
	return 0;	
}


非零段划分

题意:

自然数数组 A A A,对于区间 [ i , j ] [i,j] [i,j],若都有 A [ k ] > 0 A[k]>0 A[k]>0 ( i ≤ k ≤ j ) (i \le k \le j) (ikj) A [ i − 1 ] = A [ j + 1 ] = 0 A[i-1] = A[j+1] = 0 A[i1]=A[j+1]=0,则称位一个非零段。任选一个正数 p p p,将数组中所有小于 p p p的值变为0,求最大非零段数目。
n ≤ 5 × 1 0 5 n \le 5\times10^5 n5×105 A [ i ] ≤ 1 0 4 A[i] \le 10^4 A[i]104

解析:

对于相邻的数组元素 A [ i ] , A [ i + 1 ] A[i],A[i+1] A[i],A[i+1],若 A [ i ] < A [ i + 1 ] A[i]<A[i+1] A[i]<A[i+1],将 A [ i ] A[i] A[i]变为 0 0 0,可以使非零段的数目增加 1 1 1,此时 p p p的取值为 A [ i ] + 1 A[i]+1 A[i]+1 A [ i + 1 ] A[i+1] A[i+1],所以可以将区间 [ A [ i ] + 1 , A [ i + 1 ] ] [A[i]+1,A[i+1]] [A[i]+1,A[i+1]]的值都加 1 1 1,最后答案为 m a x [ 1... m a x n ] max{ [1...maxn]} max[1...maxn] m a x n maxn maxn是数组元素的最大值。
区间修改+区间查询,可以用线段树来维护。时间复杂度: O ( n l o g m ) O(nlogm) O(nlogm)

代码:
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 1e4+10;
const int N = 5e5+10;
int a[N];
int n, maxx, ans;
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
struct tree{
	int v;
	int tag;
}t[maxn<<2];
void pushup(int k){
	t[k].v = max(t[ls(k)].v, t[rs(k)].v);
}
void pushdown(int k){
	if(t[k].tag){
		int tag = t[k].tag;
		t[ls(k)].tag += tag;
		t[rs(k)].tag += tag;
		t[ls(k)].v += tag;
		t[rs(k)].v += tag;
		t[k].tag = 0;
	}
}
void modify(int k, int l, int r, int x, int y, int val){
	if(x<=l && y>=r){
		t[k].v += val;
		t[k].tag += val;
		return;
	}
	pushdown(k);
	int mid = (l+r) >> 1;
	if(x <= mid)
		modify(ls(k), l, mid, x, y, val);
	if(y > mid)
		modify(rs(k), mid+1, r, x, y, val);
	pushup(k);
}
int query(int k ,int l, int r, int x, int y){
	if(x<=l && y>=r){
		return t[k].v;
	}
	pushdown(k);
	int mid = (l+r) >> 1;
	int res = 0;
	if(x <= mid)
		res = max(res, query(ls(k), l, mid, x, y));
	if(y > mid)
		res = max(res, query(rs(k), mid+1, r, x, y));
	return res;
}
int main(){
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		maxx = max(maxx, a[i]);
	}
	for(int i = 0; i <= n; i++){
		if(a[i] < a[i+1])
			modify(1, 1, maxx, a[i]+1, a[i+1], 1);
	}
	ans = query(1, 1, maxx, 1, maxx);
	cout << ans << endl;
	return 0;
}


收集卡牌

题意:

n n n张不同的牌,每次抽到牌 i i i 的概率为 p i p_i pi。如果抽到已经抽过的卡则获得一枚硬币, k k k枚硬币可以换一张没有的卡。直到所有卡均获得。求抽卡次数期望。

解析:

数据范围 n ≤ 16 n\le16 n16,可以状态压缩和记忆化搜索。

设当前拥有的牌为 s t a t e state state,已经抽了 j j j次,还要抽的次数期望为 d p s t a t e , j dp_{state,j} dpstate,j

如果拥有的牌加上硬币兑换的牌是总牌数,则抽牌次数期望为 j j j

对下一次抽牌。第 j + 1 j+1 j+1抽若抽到已经有的牌,则硬币加一,拥有的牌不变;若抽到没有的牌,硬币不变,牌发生改变。

  • 输出到小数点后10位!!!
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = (1<<17)+10;
db dp[maxn][80], p[20];
int n, k;
double dfs(int state, int j, int cnt){//抽j次, cnt张不同卡 
	if(dp[state][j])
		return dp[state][j];
	if(cnt+(j-cnt)/k == n)
		return (double)j;
	double res = 0;
	for(int i = 0; i < n; i++){
		if(state&(1<<i))
			res += p[i]*dfs(state, j+1, cnt);
		else
			res += p[i]*dfs(state+(1<<i), j+1, cnt+1);
	}
	dp[state][j] = res;
	return res;
}
int main(){
	cin >> n >> k;
	for(int i = 0; i < n; i++)
		cin >> p[i];
	printf("%.10lf", dfs(0, 0, 0));
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值