【牛客练习赛87 E】贪吃蛇
题目
题目链接:https://ac.nowcoder.com/acm/contest/11177/E
牛牛在玩贪吃蛇,玩贪吃蛇吃掉别的蛇就会变长。牛牛的蛇被别人吃掉后,发现可以看广告复活,且复活后长度不变。
牛牛想到了一个作弊的高招,他和他邀请的 \(n-1\) 个朋友进入同一个房间玩贪吃蛇,初始时一共有 \(n\) 条蛇,第 \(i\) 条蛇的长度为 \(L_i\)。
进入游戏后,首先牛牛从这 \(n\) 条蛇中选择一条蛇作为自己的蛇,然后他的第 \(1\) 个朋友从剩下的 \(n-1\) 条蛇中选择一条蛇,然后他的第 \(2\) 个朋友从剩下的 \(n-2\) 条蛇中选择一条蛇 \(\cdots\) 直到牛牛的第 \(n-1\) 个朋友选择蛇后,游戏开始,现在他们互相吃掉对方。
设蛇 \(A\) 此时的长度 \(L_A\) ,蛇 \(B\) 此时的长度 \(L_B\) ,蛇 \(A\) 吃掉蛇 \(B\) 后蛇 \(A\) 长度会变为 \(L_A+L_B\),而蛇 \(B\) 则需要看 \(x\) 秒广告复活。
牛牛的朋友都会帮助牛牛使牛牛的蛇变得尽可能的长,牛牛想知道,\(M\) 秒后牛牛的蛇最长可以是多长?(注意从时刻 \(0\) 开始就可以吃了)
(假设蛇吃蛇不花时间,可以无限次复活,除了吃蛇没有其它方式可以变长,房间里除了牛牛和牛牛的朋友外没有别人)
由于答案可能很大,你只需要输出答案对 \(10^9+7\) 取模的结果。多测。
\(Q,n\leq 50\),\(1\leq L_i,x,M\leq 10^6\)。
思路
首先一秒内可以进行任意次吃的操作,不难发现,如果某个时刻存在两条蛇都活着,那么肯定是其中一条立刻吃掉另一条。如果拖后了再吃或者不吃肯定都不会更优。
所以这个 \(m\) 和 \(x\) 都是假的,其实就是说可以进行 \(m'=\frac{m}{x}+1\) 轮互吃,且每一轮都只剩一下一条活着。
而且我们也不用去关心牛牛选的是哪一条蛇,最后活下来的那一只就给牛牛即可。所以我们只关心最大化 \(m'\) 轮后 \(L\) 中的最大值。
对于初始的序列 \(L\),把他从小到大排序,如果只能合并一次的话,容易发现是让 \(n-1\) 吃 \(n\),\(n-2\) 吃 \(n-1\),一直到 \(1\) 吃 \(2\) 这样最优。
而吃完后这个序列从单调不降变为了单调不增,所以如果还需要继续合并,那么就是从前往后依次合并。那么其实就是一直重复从后往前,从前往后这两个合并的过程。
因为 \(n\leq 50\),所以直接上矩阵快速幂即可。如果 \(m'\) 是奇数,最后还需要多乘一次。
时间复杂度 \(O(Qn^3\log m)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=52,MOD=1e9+7;
int Q,n,t,m;
struct Matrix
{
int a[N][N];
friend Matrix operator *(Matrix a,Matrix b)
{
Matrix c;
memset(c.a,0,sizeof(c.a));
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
for (int k=1;k<=n;k++)
c.a[i][j]=(c.a[i][j]+1LL*a.a[i][k]*b.a[k][j])%MOD;
return c;
}
}a,b,f;
void fpow(Matrix &a,Matrix &f,int k)
{
for (;k;k>>=1,a=a*a)
if (k&1) f=f*a;
}
int main()
{
scanf("%d",&Q);
while (Q--)
{
memset(f.a,0,sizeof(f.a));
memset(a.a,0,sizeof(a.a));
memset(b.a,0,sizeof(b.a));
scanf("%d%d%d",&n,&t,&m);
m=(m)/t+1;
for (int i=1;i<=n;i++)
scanf("%d",&f.a[1][i]);
sort(f.a[1]+1,f.a[1]+1+n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
a.a[i][j]=b.a[n-i+1][n-j+1]=(i>=j);
b=a*b; fpow(b,f,m/2);
if (m&1)
{
f=f*a;
cout<<f.a[1][1]<<"\n";
}
else cout<<f.a[1][n]<<"\n";
}
return 0;
}