2016 Hdu 多校赛第五场

Hdu 5781 ATM Mechine

链接

ATM Mechine

题意

Alice在银行的存款不超过K元,如果她取的钱超过了她在银行存款,就会被警告。
她最多可被警告W次,问你采用最优策略之后,期望取完所有钱的次数是多少

分析

E(i,j):存款的范围是[0,i],还可以被警告j次的期望值
取款金额为k时,有两种情况:没被警告,则余额大于等于k元,概率为(i-k+1)/(i+1)
被警告,则余额小于k元,概率为k/(i+1),则转移方程:
$E(i,j)=min_{k=1}^{i}{\frac{i-k+1}{i+1} * E(i-k,j)+\frac{k}{i+1}*E(k-1,j-1)+1}$
Alice使用的是二分策略,那么在最坏情况下至多被警告$\left \lceil log_{2}{K} \right \rceil$
于是W:=min(W,15)就可以了

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<stdio.h>
#include<string.h>
#define min(a,b) a<b?a:b
const double INF=1e9;
const int N=2010;
double dp[N][20];
void init()
{

for(int i=0;i<N;i++){
for(int j=0;j<16;j++){
if(i==0)
dp[i][j]=0;
else
dp[i][j]=INF;
}
}
for(int i=1;i<N;i++){
for(int j=1;j<16;j++){
for(int k=1;k<=i;k++){
dp[i][j]=min(dp[i][j],dp[i-k][j]*(i-k+1)/(i+1)+dp[k-1][j-1]*k/(i+1)+1);
}
}

}
}
int main()
{

int k,w;
init();
while(scanf("%d%d",&k,&w)!=EOF){
int tm=min(w,15);
printf("%.6lf\n",dp[k][tm]);
}
return 0;
}

Hdu 5783 Divide the Sequence

链接

Divide the Sequence

题意

把长度为n的序列分成尽量多的连续段,使得每一段的每个前缀和都不小于0。保证有解。

分析

从后往前贪心分段即可。

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<stdio.h>
#include<string.h>
const int N=1000010;
int a[N];
int main()
{

int n;
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
long long sum=0;
int ans=0;
for(int i=n;i>=1;i--){
sum+=a[i];
if(sum>=0){
sum=0;
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}

Hdu 5791 Two

链接

Two

题意

已知A,B两个串,求A,B公共子序列的个数

分析

dp[i][j]表示A序列前i个数和B序列前j个数的相同子序列对的个数。
则 若a[i]!=b[j],dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1];
否则dp[i][j]=dp[i-1][j]+dp[i][j-1]+1;

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<stdio.h>
#include<string.h>
typedef long long LL;
const int N=1005;
const int MOD=(int)1e9+7;
LL dp[N][N];
int a[N],b[N];
LL solve(int n,int m)
{

memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j]=(dp[i-1][j]+dp[i][j-1])%MOD;
if(a[i]==b[j])
dp[i][j]=(dp[i][j]+1)%MOD;
else
dp[i][j]=(dp[i][j]-dp[i-1][j-1]+MOD)%MOD;
}
}
return dp[n][m];
}
int main()
{

int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
scanf("%d",&b[i]);
printf("%lld\n",solve(n,m));
}
return 0;
}

Hdu 5792 World is Exploding

链接

World is Exploding

题意

给一个长度n的序列A,问有多少四元组(a,b,c,d)满足:4个数两两不同,
$1 <= a < b <= n,1 <= c < d <= n,A_a < A_b,A_c > A_d$

分析

定义:$rg(x)=∣{y∣x < y <= n,Ax < Ay}|$ 在Ax右边比它大的数的个数
$lg(x)=∣{y∣1 <= y < x,Ax < Ay}| $在Ax左边比它大的数的个数
$rs(x)=∣{y∣x < y <= n,Ax > Ay}| $在Ax右边比它小的数的个数
$ls(x)=∣{y∣1 <= y< x,Ax > Ay}| $在Ax左边比它小的数的个数
则answer=顺序对个数*逆序对个数-不符合情况的个数
不符合的情况分四种:
a=c时,$a < b,a < d,Aa < Ab,Aa > Ad$,个数=rg*rs,
a=d时,$a < b,c < a,Aa < Ab,Ac > Aa$,个数=rg*lg,
b=c时,$a < b,b < d,Aa < Ab,Ab > Ad$,个数=ls*rs,
b=d时,$a < b,c < b,Aa < Ab,Ac > Ab$,个数=ls*lg
以上值都可通过树状数组或线段树求出,题目数据较大,需离散化处理

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=50010;
const int MOD=(int)1e9+7;
int n,tree[N];
int ls[N],rs[N],lg[N],rg[N];
struct stu{
int val;
int id;
int rk;
}a[N];
bool cmp1(stu a,stu b)
{

return a.val<b.val;
}
bool cmp2(stu a,stu b)
{

return a.id<b.id;
}
int lowbit(int x)
{

return x&(-x);
}
void update(int pos)
{

for(int i=pos;i<=n;i+=lowbit(i))
tree[i]++;
}
int query(int pos)
{

int ans=0;
for(int i=pos;i>=1;i-=lowbit(i))
ans+=tree[i];
return ans;
}
int main()
{

while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++){
scanf("%d",&a[i].val);
a[i].id=i;
}
sort(a+1,a+1+n,cmp1);
a[0].val=-1;
int cnt=1;
for(int i=1;i<=n;i++){ //离散化
if(a[i].val==a[i-1].val)
a[i].rk=a[i-1].rk;
else
a[i].rk=cnt;
cnt++;
}
sort(a+1,a+1+n,cmp2);
memset(tree,0,sizeof(tree));
LL sumls=0,sumrs=0;
for(int i=1;i<=n;i++){
ls[i]=query(a[i].rk-1);
int tmp=query(a[i].rk);
lg[i]=i-1-tmp;
sumls+=ls[i];
update(a[i].rk);
}
memset(tree,0,sizeof(tree));
for(int i=n;i>=1;i--){
rs[i]=query(a[i].rk-1);
int tmp=query(a[i].rk);
rg[i]=n-i-tmp;
sumrs+=rs[i];
update(a[i].rk);
}
LL ans=sumls*sumrs;
for(int i=1;i<=n;i++){
ans-=(ls[i]*rs[i]+rg[i]*lg[i]+ls[i]*lg[i]+rg[i]*rs[i]);
}
printf("%lld\n",ans);
}
return 0;
}