首页 > 其他分享 >P7137 [THUPC2021 初赛] 切切糕 题解

P7137 [THUPC2021 初赛] 切切糕 题解

时间:2024-03-27 17:45:53浏览次数:30  
标签:sort frac 题解 ll long 初赛 THUPC2021 Sum define

题目传送门

前置知识

博弈论

解法

由于本题是 CF1628D1 Game on Sum (Easy Version) 的扩展,故先从 CF1628D1 Game on Sum (Easy Version) 讲解。

CF1628D1 Game on Sum (Easy Version)

设 \(x_{i}\) 表示第 \(i\) 轮时 Alice 选择的数。

设 \(f_{i,j}\) 表示已经进行了 \(i\) 轮,且使用了 \(j\) 次加法时的最大得分,状态转移方程为 \(f_{i,j}= \max \{ \min(f_{i-1,j}-x_{i},f_{i-1,j-1}+x_{i}) \}=\frac{f_{i-1,j}+f_{i-1,j-1}}{2}\),边界为 \(\begin{cases} f_{i,0 \sim \infty}=0 & i=0 \\ f_{i,0}=0, f_{i,i}=i \times k & i \ne 0 \end{cases}\)。

  • 由于 Bob 想让结果尽可能小,所以有 \(f_{i,j}= \min(f_{i-1,j}-x_{i},f_{i-1,j-1}+x_{i})\)。
  • 由于 Alice 想让结果尽可能大,所以会让 \(\min(f_{i-1,j}-x_{i},f_{i-1,j-1}+x_{i})\) 取到最大值,即 \(f_{i-1,j}-x_{i}=f_{i-1,j-1}+x_{i}\) 时,解得 \(x_{i}= \frac{f_{i-1,j}-f_{i-1,j-1}}{2}\),代入原式有 \(f_{i,j}=\frac{f_{i-1,j}+f_{i-1,j-1}}{2}\)。

由于 Bob 想让结果尽可能小,所以至多使用 \(m\) 次加法,故最终 \(f_{n,m}\) 即为所求。

另外,由于求解 \(f_{n,m}\) 的过程中只有加法和 \(\times \frac{1}{2}\) 运算,故可以将 \(k\) 缩小至 \(1\) 进行预处理 \(f_{n,m}\),询问时再扩大到 \(k\),即 \(f_{n,m} \times k\)。

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define ull unsigned long long
#define sort stable_sort 
#define endl '\n'
const ll p=1000000007;
ll f[2010][2010];
ll qpow(ll a,ll b,ll p)
{
    ll ans=1;
    while(b>0)
    {
        if(b&1)
        {
            ans=ans*a%p;
        }
        b>>=1;
        a=a*a%p;
    }
    return ans;
}
int main()
{
    ll t,n,m,k,i,j;
    cin>>t;
    for(i=1;i<=2000;i++)
    {
        f[i][0]=0;
        f[i][i]=i;
        for(j=1;j<=i-1;j++)
        {
            f[i][j]=((f[i-1][j]+f[i-1][j-1])%p)*qpow(2,p-2,p)%p;
        }
    }
    for(i=1;i<=t;i++)
    {
        cin>>n>>m>>k;
        cout<<f[n][m]*k%p<<endl;
    }
    return 0;
}

luogu P7137 [THUPC2021 初赛] 切切糕

从贪心的角度分析, Tinytree 的“优先选糕权”要尽量留给 \(a_{i}\) 较大的切糕,故需要先将 \(a\) 按照降序排序。

设第 \(i\) 块切糕 Kiana 切成的切糕大小为 \(x_{i}\) 和 \(a_{i}-x_{i}\),规定有 \(x_{i} \ge a_{i}-x_{i}\)。

设 \(f_{i,j}\) 表示已经切了 \(i\) 块切糕,且使用了 \(j\) 次“优先选糕权”时 Tinytree 的最大总大小,状态转移方程为 \(f_{i,j}= \max \{ \min(f_{i-1,j}+a_{i}-x_{i},f_{i-1,j-1}+x_{i}),f_{i-1,j} \}=\max(\frac{f_{i-1,j}+f_{i-1,j-1}+a_{i}}{2},f_{i-1,j})\),边界为 \(\begin{cases} f_{i,0 \sim \infty}=0 & i=0 \\ f_{i,0}=0, f_{i,i}=\frac{\sum_{j=1}^{i}a_{j}}{2} & i \ne 0 \end{cases}\)。

  • \(x_{i}\) 的求解同 CF1628D1 Game on Sum (Easy Version)
  • 由于算出的 \(x_{i}\) 可能使 \(a_{i}-x_{i}<0\) 成立,故最后需要与 \(f_{i-1,j}\) 取 \(\max\)。

由于 Tinytree 想让 Kiana 的总大小尽可能小,所以一定会使用 \(m\) 次“优先选糕权”,使自己的总大小尽可能大,故最终 \(\sum\limits_{i=1}^{n}a_{i}-f_{n,m}\) 即为所求。

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define ull unsigned long long
#define sort stable_sort 
#define endl '\n'
ll a[2510],sum[2510];
double f[2510][2510];
int main()
{
    ll n,m,i,j;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    sort(a+1,a+1+n,greater<ll>());
    for(i=1;i<=n;i++)
    {
        sum[i]=sum[i-1]+a[i];
    }
    for(i=1;i<=n;i++)
    {
        f[i][0]=0;
        f[i][i]=1.0*sum[i]/2;
        for(j=1;j<=i-1;j++)
        {
            f[i][j]=max((f[i-1][j]+f[i-1][j-1]+1.0*a[i])/2,f[i-1][j]);
        }
    }
    printf("%.6lf",sum[n]-f[n][m]);
    return 0;
}

标签:sort,frac,题解,ll,long,初赛,THUPC2021,Sum,define
From: https://www.cnblogs.com/The-Shadow-Dragon/p/18099859

相关文章

  • Spring Boot 工程开发常见问题解决方案,日常开发全覆盖
    本文是SpringBoot开发的干货集中营,涵盖了日常开发中遇到的诸多问题,通篇着重讲解如何快速解决问题,部分重点问题会讲解原理,以及为什么要这样做。便于大家快速处理实践中经常遇到的小问题,既方便自己也方便他人,老鸟和新手皆适合,值得收藏......
  • 【赛题解析】【网络建设与运维】第三阶段Linux Vsftpd部分答案解析
    培训、环境、资料、考证公众号:波比网络公众号2:波比网络工作室网络建设与运维群:685678820波比网络专注于技能提升,赋能ftp服务任务描述:请采用ftp服务器,实现文件安全传输。1.配置 linux1为ftp服务器,安装vsftpd,新建本地用户xiaoming,本地用户登陆ftp后的目录为/var/ft......
  • CF1879的题解
    (一)对于第一个问题,直接搜出字符串中有多少个仅由\(0\)或\(1\)组成的串组成的。对于第二个问题,每个串只有一个能选,然后选择顺序有所不同,具体看代码。(二)AC代码。#defineintlonglong#definemd998244353usingnamespacestd;intt,s[200010];charch[200010];sign......
  • CF340B的题解
    (一)枚举对角线。然后分别找正在对角线上方的点与对角线端点构成三角形面积的最大值。和在对角线下方的点与对角线端点构成三角形面积的最大值。如果所有点都在同侧,那么不算。通过过两点直线的解析式求出另一点在直线的哪一侧。(二)AC代码。#include<bits/stdc++.h>#define......
  • CF1864C的题解
    (一)可以将\(x\)转为二进制。考虑一个数的二进制\((1\dots10\dots0)\)。其中,第一个省略号中有什么不确定,第二个省略号里都是\(0\)。易得,每个数都可以看成这种形式。那么可以每次去掉最后一位的\(1\),易证减去的数是原数的因数。最后会得到形如\((10\dots0)\),省略号中全是......
  • AT_abc345_c的题解
    (一)首先交换相同字符不改变字符串形态,那么就先统计是否有相同字符。交换不同字符容易证明不同操作后字符串各不相同。用前缀和或后缀和维护\(i+1\)到\(n\)中与\(i\)位置字符不同的数量。(二)AC代码。#include<bits/stdc++.h>#defineintlonglongusingnamespacestd......
  • AT_abc344_e的题解
    (一)这次ABC有点水。每个数记录前面那个数,和后面那个数。对于每个数,开个数组记录值,用map记录一个值的位置(二)AC代码。#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;intpre[400010],cnt,las[400010],a[400010],n,q;map<int,int>mp;signedmain(......
  • CF1923B的题解
    (一)注意到\(x_i\)的绝对值\(\len\)。那么统计每一个位置的血量和。先从靠近的击杀必定最优,剩余的子弹用\(sum\)存储。还是直接上代码吧。(二)AC代码。#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;intt,n,k,a[300010],dis[300010],s[300010];......
  • AT_abc344_c的题解
    (一)数据范围较小,三重循环枚举选的数,用map存储可能的和即可。(二)AC代码。#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;intn,m,l,q,a[110],b[110],c[110];map<int,bool>mp;signedmain(){ scanf("%lld",&n); for(inti=1;i<=n;i++)scan......
  • CF1195D2的题解
    (一)虽说代码较长,但非常好理解,还是最优解(公开的就两个)。考虑对每个数单独算贡献,循环枚举与它进行运算的数的长度,然后确定那个数的位置即可,再乘以出现的数位对应的贡献,如出现在倒数第二位就乘\(10\)。难度应该不到绿。(二)AC代码。#include<bits/stdc++.h>#defineintlonglo......