[HDU 7086]Pty plays game
\(Pty plays game\)
題目大意
玩家有 \(n\) 個士兵, \(BOSS\) 有 \(m\) 個士兵,兩邊的士兵站成一個佇列。
遊戲過程如下:
-
兩邊隊頭的士兵相互攻擊,當一名士兵陣亡時,他所在佇列的下一個士兵會替補登場,若 \(BOSS\) 的士兵全部陣亡而玩家的士兵還活著,玩家勝利。
-
每個士兵有 \(h\) 的生命值,單位時間內能對敵造成的傷害為 \(d\) 。
開戰之前,可以選擇 \(x\) 天的訓練時間,每訓練一天,士兵 \(x\) 的生命值與攻擊力會增長 \(dh_x\)與\(dd_x\) 。
問是否 \(\exists x\in [0,10^{18}]\) 使得玩家勝利,若存在,輸出最小的 \(x\)
分析
首先,對於這道題目,我們很容易向二分的方向去想,但我們並不知道其是否具有單調性,同時,我們不能在短時間內判斷一個局面合法與否,考慮一次解決這些問題。
判斷局面勝敗
首先,我們注意到,如果玩家和 \(BOSS\) 都只有一個士兵,分別為 \((h_1,d_1)\),\((h_2,d_2)\) 則玩家打死 \(BOSS\) 的士兵所用的時間 \(T_1=\frac{h_2}{d_1}\) ,同理, \(BOSS\) 打死玩家的士兵所用的時間為 \(T_2=\frac{h_1}{d_2}\) 。
當 \(T_1<T_2\) 即 \(h_1\times d_1>h_2\times d_2\)
考慮擴充套件這個局面,給 \(BOOS\) 增加一個士兵為 \((h_3,d_3)\) ,而戰勝 \(BOSS\) 第一個士兵後,玩家的士兵狀態變為 \((h_1-\frac{h_2}{d_1}\times d_2,d_1)\) 。
重複上面的過程,式子展開後我們發現玩家想要獲勝需要 \(h_1\times d_1>h_2\times d_2+h_3\times d_3\) 。
那麼一般的,若一個局面滿足:
\[h_1\times d_1+h_2\times d_2+……+h_n\times d_n>h_{n+1}\times d_{n+1}+h_{n+2}\times d_{n+2}+……+h_{n+m}\times d_{n+m} \]則玩家獲勝。
化簡求值
那麼,對於一個合法的 \(x\) ,滿足\(\sum_{i=1}^n (h_i+x\times dh_i)\times(d_i+x\times dd_i)>\sum_{i=n+1}^{n+m} (h_i+x\times dh_i)\times(d_i+x\times dd_i)\)
發現可以把兩邊展開,化為一個二次函式 \(Ax^2+Bx+C>0\) ,求滿足條件的最小 \(x\)。
於是我們可以展開分類討論,進行求解。
-
當 \(A\neq 0\) 時二次函式需要用到二分查詢(當然也可以直接求根公式,
如果你能把精度卡過的話)。 -
當 \(A=0\) 時一次函式可以直接求根,注意如果是小數應該向哪邊取整。
-
當 \(A=0\) 且 \(B=0\) 時,函式值不變,直接看 \(C\) 即可。
注:注意精度
CODE
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w*=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
struct node{
int x,y,dx,dy;
}a[N],b[N];
int T;
signed main()
{
// freopen("data1.in","r",stdin);
// freopen("data.out","w",stdout);
T=read();
while(T--){
int n=read(),m=read();
for(register int i=1;i<=n;i++){
a[i].x=read(),a[i].dx=read();
a[i].y=read(),a[i].dy=read();
}
for(register int i=1;i<=m;i++){
b[i].x=read(),b[i].dx=read();
b[i].y=read(),b[i].dy=read();
}
int A=0,B=0,C=0;
for(register int i=1;i<=n;i++){
A=A+a[i].dx*a[i].dy;
B=B+a[i].y*a[i].dx+a[i].x*a[i].dy;
C=C+a[i].x*a[i].y;
}
for(register int i=1;i<=m;i++){
A=A-b[i].dx*b[i].dy;
B=B-b[i].y*b[i].dx-b[i].x*b[i].dy;
C=C-b[i].x*b[i].y;
}
// printf("%lld %lld %lld\n",A,B,C);
if(!A&&!B){
if(C>0) puts("0");
else puts("none");
continue;
}
if(!A){
int ans=(-C)/B;
if(B>0){
ans++;
if(C>0) puts("0");
else if(C==0) puts("1");
else printf("%lld\n",ans);
}
else{
if(C>0) puts("0");
else puts("none");
}
}
else{
if(A>0){
if(C>0) puts("0");
else{
long double l=0,r=1e18;
int ans=-1;
while(l<=r){
int mid=(l+r)/2;
if(A*(long double)mid*(long double)mid+B*(long double)mid+C>0) ans=mid,r=mid-1;
else l=mid+1;
}
if(ans==-1) puts("none");
else printf("%lld\n",ans);
}
}
else{
if(C>0) puts("0");
else{
double p=-(double)B/(double)(2*A);
if(p<0) puts("none"); //對稱軸小於0
else{
double mx=(double)A*p*p+(double)B*p+C;
//if(mx<0) { puts("none");
long double l=0,r=(int)p;
int ans=-1;
while(l<=r){
int mid=(l+r)/2;
if(A*(long double)mid*(long double)mid+B*(long double)mid+C>0) ans=mid,r=mid-1;
else l=mid+1;
}
if(ans==-1) puts("none");
else printf("%lld\n",ans);
}
}
}
}
}
return 0;
}
作者: ╰⋛⋋⊱๑落葉๑⊰⋌⋚╯
-------------------------------------------
海到無邊天作岸,山登絕頂我為峰!