数组推导
题意:
自然数数组 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[i−1]时,
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)
0≤A[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)
(i≤k≤j)且
A
[
i
−
1
]
=
A
[
j
+
1
]
=
0
A[i-1] = A[j+1] = 0
A[i−1]=A[j+1]=0,则称位一个非零段。任选一个正数
p
p
p,将数组中所有小于
p
p
p的值变为0,求最大非零段数目。
n
≤
5
×
1
0
5
n \le 5\times10^5
n≤5×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 n≤16,可以状态压缩和记忆化搜索。
设当前拥有的牌为 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;
}