[CSP-S模拟测试]:赤(red)(WQS二分+DP)

题目传送门(内部题38)


输入格式

每个输入文件包含多组测试数据。选手应当处理到文件结束($EOF$)
每一组数据包括$3$行。
第$1$行包含三个正整数$n,a,b$,表示有$n$只猫,$gyz$有$a$包干脆面和$b$包豆干。
第$2$行包含$n$个保留小数点后$3$位的实数$p_1,p_2...p_n$,$p_i$表示第$i$只猫喜欢干脆面的概率。
第$3$行包含$n$个保留小数点后$3$位的实数$q_1,q_2...q_n$,$q_i$表示第$i$只猫喜欢豆干的概率。


输出格式

每组测试数据输出一行,表示最优策略下期望捉到的猫数。保留小数点后$3$位输出。


样例

样例输入:

3 2 2
1.000 0.000 0.500
0.000 1.000 0.500
4 1 3
0.100 0.500 0.500 0.600
0.100 0.500 0.900 0.400
3 2 0
0.412 0.198 0.599
0.612 0.987 0.443

样例输出:

2.75000
2.16000
1.01100


数据范围与提示

$T$表示测试数据组数,$\sum n$表示输入文件中所有数据的$n$之和。
$100\%$的数据,$T\leqslant 10,\sum n$不超过$100,000$。
$100\%$的数据,$0\leqslant a\leqslant n,0\leqslant b\leqslant n$。
每个测试点的$n,T$的范围见下表。


题解

原题是$codeforces 794E$,这道题增加了数据范围i提高了难度(原题官方题解$\Theta(n^2\log n)$时间复杂度).

为了方便,以下干脆面简称辣条……

对于这道题,我们期望抓到猫的个数即为猫喜欢的概率。

那么对于一只猫,分为四种情况:

  $\alpha.$不投喂,贡献为$0$。

  $\beta.$投喂辣条,贡献为$p_i$。

  $\gamma.$投喂豆干,贡献为$q_i$。

  $\delta.$都投喂,贡献为$p_i+q_i-p_i\times q_i$(注意可以理解为减去了都喜欢的概率)。

先来考虑$\Theta(n\times a\times b)$的$DP$。

定义$dp[i][j][k]$表示到了第$i$只猫,用了$j$个辣条,$k$个豆干的最大值。

那么状态转移方程即为:
$dp[i][j][k]=\max(dp[i-1][j][k],dp[i-1][j-1][k-1]+p_i+q_i-p_i\times q_i,dp[i-1][j-1][k]+p_i,dp[i-1][j][k-1]q_i)$

需要注意的是空间问题,小心不要$MLE$,不然你就$TM$双$LE$了。

不知道有没有人还记得这道题[BZOJ2654]:tree(Kruskal+WQS二分),当然我说的是正解,也就是$WQS$(忘情水)二分。

那么这道题我们也可以这样考虑,先将所有的辣条都加一个额外的代价,然后用花费的辣条跟总辣条数做比较;豆干同理。

时间复杂度:$\Theta(n\log^2n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,a,b;
double p[100001],q[100001];
double dp[100001],fp[100001],fq[100001];
void calc(double x,double y)
{
	for(int i=1;i<=n;i++)
	{
		dp[i]=dp[i-1];
		fp[i]=fp[i-1];
		fq[i]=fq[i-1];
		if(dp[i-1]+p[i]>dp[i]+x)
		{
			dp[i]=dp[i-1]+p[i]-x;
			fp[i]=fp[i-1]+1;
			fq[i]=fq[i-1];
		}
		if(dp[i-1]+q[i]>dp[i]+y)
		{
			dp[i]=dp[i-1]+q[i]-y;
			fp[i]=fp[i-1];
			fq[i]=fq[i-1]+1;
		}
		if(dp[i-1]+p[i]+q[i]-p[i]*q[i]>dp[i]+x+y)
		{
			dp[i]=dp[i-1]+p[i]+q[i]-p[i]*q[i]-x-y;
			fp[i]=fp[i-1]+1;
			fq[i]=fq[i-1]+1;
		}
	}
}
double dichotomize2(double mid1)
{
	double lft=0,rht=1;
	while(rht-lft>1e-8)
	{
		double mid2=(lft+rht)/2;
		calc(mid1,mid2);
		if(fq[n]>b)lft=mid2;
		else rht=mid2;
	}
	return rht;
}
pair<double,double> dichotomize1()
{
	double lft=0,rht=1;
	while(rht-lft>1e-8)
	{
		double mid=(lft+rht)/2;
		calc(mid,dichotomize2(mid));
		if(fp[n]>a)lft=mid;
		else rht=mid;
	}
	double flag=dichotomize2(rht);
	calc(rht,flag);
	return make_pair(rht,flag);
}
int main()
{
	while(~scanf("%d%d%d",&n,&a,&b))
	{
		for(int i=1;i<=n;i++)scanf("%lf",&p[i]);
		for(int i=1;i<=n;i++)scanf("%lf",&q[i]);
		pair<double,double> flag=dichotomize1();
		printf("%.3lf\n",dp[n]+a*flag.first+b*flag.second);
	}
	return 0;
}

rp++

猜你喜欢

转载自www.cnblogs.com/wzc521/p/11496735.html