一個簡單的貪心,回憶一下現實:當選擇任何的一個東西的代價都相同時,我們肯定會優先地選擇效益(價值)最好的一個(相信大家都是這樣的吧)。
回到本題,因爲選擇任何一個魔法的代價都是一樣的(都是 ),我們可以貪心地優先選擇那些數值大的魔法。爲什麼呢?
假設當前魔法值爲 ,則 Sisyphus
爬到 位置時會掉落到原點,然後他會用爬 的距離到達山頂。則他爬的路程就是 。因爲他的速度不變,則時間和路程成正比。所以我們需要讓 儘可能的大,又因爲 不變,所以我們只需讓 儘可能大即可。
故,我們只需貪心地優先選擇位置值大的魔法即可解決本題。
但是,每次枚舉會 TLE
,我們可以用前綴和的方法預處理出在使用 個魔法的前提下,可以讓 Sisyphus
走多遠的 。
爲什麼是距離而不是時間?因爲時間可能是小數,但距離一定是整數。注意需要隨時把輸入的時間轉化爲路程,否則會 WA
。
#define ll long long
const int N=2e5+100;
ll a[N],s[N],n,q,L,v;
bool cmp(ll t1,ll t2){
return t1>t2;
}
int main(){
// freopen("endless.in","r",stdin);
// freopen("endless.out","w",stdout);
n=read();L=read();v=read();
for(int i=1;i<=n;i++)
a[i]=read();
sort(a+1,a+n+1,cmp);
s[0]=L;//注意初始化!
for(int i=1;i<=n;i++)
s[i]=s[i-1]+a[i];
q=read();//輸入詢問的數量
for(int i=1;i<=q;i++){
register ll t=read();
if (t*v<L) printf("0\n");
else if (t*v>=s[n]) printf("-1\n");
else{
int l=upper_bound(s+1,s+n+1,t*v)-s;
printf("%d\n",l);
}
}
return 0;
}
本思路可能只能通過洛谷的民間數據……
我們一看題目,就知道這題肯定要用到搜索算法。數據提示我們,我們要用 bfs
算法。
我們把隱身和瞬移的次數也寫入狀態中,則我們可以一遍 bfs
求出這兩個量。
每次判斷一個點是否是士兵的觀察點太慢,於是我們可以提前把所有的點是否可以被士兵觀察到這麼一個狀態計算出來,存入數組中, 判斷。
代碼可以通過洛谷的民間數據(不能通過的時候告訴我)。建議使用標程的方法。
struct node{
int x,y,step,c1,c2;
};bool b[360][360][17][17];
int a[360][360],n,m,c1,c2,d;
inline bool Check(int x,int y){
return !(x<1||x>n||y<1||y>m);
}
inline bool check(int x,int y){
if (!Check(x,y)) return false;
if (a[x][y]>0) return false;
return true;
}
const int dx[]={1,0,-1,0};int bx;
const int dy[]={0,1,0,-1};int by;
int ans_step,ans_c1,ans_c2,ex,ey;
const int Dx[]={0,0,1,1,1,-1,-1,-1};
const int Dy[]={1,-1,0,1,-1,0,1,-1};
inline void bfs(int bx,int by){
node nxt,now=(node){bx,by,0,c1,c2};
queue<node> q;q.push(now);
b[bx][by][c1][c2]=true;
while (!q.empty()){
now=q.front();q.pop();
if (now.step>ans_step) break;
if (now.x==ex&&now.y==ey){
ans_step=now.step;//記錄步數
int u1=c1-now.c1,u2=c2-now.c2;
if (u1+u2<ans_c1+ans_c2){
ans_c1=u1;ans_c2=u2;
}
else if (u1<ans_c1){
ans_c1=u1;
ans_c2=u2;
}
}
for(int i=0;i<8;i++){
nxt.x=now.x+Dx[i];
nxt.y=now.y+Dy[i];
nxt.step=now.step+1;
if (!check(nxt.x,nxt.y))
continue;//越界錯誤
if (a[nxt.x][nxt.y]==-1)
nxt.c1=now.c1-1;
else nxt.c1=now.c1;
nxt.c2=now.c2;//計算新狀態
if (nxt.c1<0) continue;//不符題意
if (b[nxt.x][nxt.y][nxt.c1][nxt.c2])
continue;//已經走過,不再走另一次
b[nxt.x][nxt.y][nxt.c1][nxt.c2]=1;
q.push(nxt);
}
for(int i=0;i<4;i++){
nxt.x=now.x+dx[i]*d;
nxt.y=now.y+dy[i]*d;
nxt.step=now.step+1;
if (!check(nxt.x,nxt.y))
continue;//越界錯誤
if (a[nxt.x][nxt.y]==-1)
nxt.c1=now.c1-1;
else nxt.c1=now.c1;
nxt.c2=now.c2-1;//計算新狀態
if (nxt.c2<0||nxt.c1<0) continue;
if (b[nxt.x][nxt.y][nxt.c1][nxt.c2])
continue;//已經走過,不再走另一次
b[nxt.x][nxt.y][nxt.c1][nxt.c2]=1;
q.push(nxt);
}
}
}
const int inf=0x3f3f3f3f;
int main(){
// freopen("bandit.in","r",stdin);
// freopen("bandit.out","w",stdout);
memset(b,false,sizeof(b));
cin>>n>>m>>c1>>c2>>d;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
char c;cin>>c;
if (isdigit(c)){
register int t=c-48;
while (c=getchar()){
if (!isdigit(c)) break;
t=t*10+c-48;
}
a[i][j]=t;//不能走
}
else{
if (c=='S'){bx=i,by=j;a[i][j]=0;}
else if (c=='T'){ex=i,ey=j;a[i][j]=0;}
else a[i][j]=0;
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if (a[i][j]>0)
for(int k=0;k<a[i][j];k++)
for(int l=0;k+l<a[i][j];l++){
if (check(i-k,j-l))
a[i-k][j-l]=-1;
if (check(i+k,j-l))
a[i+k][j-l]=-1;
if (check(i-k,j+l))
a[i-k][j+l]=-1;
if (check(i+k,j+l))
a[i+k][j+l]=-1;
}
ans_step=inf;ans_c1=inf;
ans_c2=inf;bfs(bx,by);
if (ans_step==inf) printf("-1");
else{
cout<<ans_step<<" ";
cout<<ans_c1<<" ";
cout<<ans_c2;
}
return 0;
}
先考慮 棟樓,高度限制爲 ,且 時的方案數。
這個東西我們可以看做是把 個相同的小球放入 個小盒中,方案數爲 。
因爲單調不增和單調不減本質一樣(把其中一個反回來就是另外一個),所以該公式一樣使用於單調不增中。
題目未說 是否在 的兩側,分類討論:
-
當 在 的同側時,把 看做一棟樓,則該冊總方案數爲 。另一側的方案數爲 ,二者相乘即爲答案。
-
當 在 的兩側時,枚舉 (即 )。
記 ,把整個圖形分成四塊:
- 第 棟樓
- 第 棟樓
- 第 棟樓
- 第 棟樓
分開求其方案數相乘即爲 固定時的結果。把每種情況中編號最小的樓看做 號樓即可方便的求出答案。
const int N=2e5+100;
const int mod=998244353;
int inv[N],fac[N],n,m,x,y,ans;
inline int ksm(int a,int b){
register int ret=1;//注意初始化
while (b){//b還沒有被分完,繼續
if (b&1) ret=1ll*ret*a%mod;
a=1ll*a*a%mod;b>>=1;
}//x>>=a:二進制運算,等於x/=2^a
return ret;//ret:a的b次冪%mod
}//快速冪的模板,注意實時取模和1ll
inline void init_fac_and_inv(){
fac[0]=1;//求i(1<=i<=n+m)的階乘
for(int i=1;i<=n+m;i++)//正序
fac[i]=1ll*fac[i-1]*i%mod;
inv[n+m]=ksm(fac[n+m],mod-2);
for(int i=n+m-1;i>=0;i--)//倒序
inv[i]=1ll*inv[i+1]*(i+1)%mod;
// 後三行是求i(1<=i<=n+m)的階乘的逆元
// 注意第二個for的循環順序必須是倒序!
}//O(n+m)預處理出i(1<=i<=n+m)的階乘和逆元
inline int f(int n,int m){//求C(n+m-1,m-1)
return 1ll*fac[n+m-1]*inv[m-1]%mod*inv[n]%mod;
}//把n棟樓分m種不同高度的方案數,相當於n個球m個樓
int main(){
scanf("%d%d%d%d",&m,&n,&x,&y);
init_fac_and_inv();//別忘了調用
if (x>y) swap(x,y);//保證y>=x
if (!((x<=n)^(y<=n))){//同側
ans=1ll*f(n,m)*f(n+x-y,m)%mod;
}
else{//否則就是在異側,需要枚舉高度i
for(int i=1;i<=m;i++){//hx=hy=i
int tmp=1ll*f(x-1,i)*f(n-x,m-i+1)%mod;
tmp=1ll*tmp*f(y-n-1,m-i+1)%mod*f(2*n-y,i)%mod;
ans=(1ll*ans+tmp)%mod;//累加方案數(即答案)
}
}
printf("%d",ans);
return 0;
}