NOI Online #2入門組
A 未了
題目描述
由於觸犯天神,Sisyphus 將要接受懲罰。
宙斯命令Sisyphus推一塊兒巨石上長度爲L的山坡。Sisyphus勻速向上推的速度爲每年v的長度(由於是勻速, 故經過1/2年將能向上推v/2的長度)。然而, 宙斯並不希望Sisyphus太快到達山頂。宙斯可以施展n個魔法, 若他施展第i個魔法, 那麼Sisphus第一次到達位置ai時, 他將會同巨石一起滾落山底, 並從頭推起。(滾落的時間忽略不計, 即可看做第一次到達位置ai後, Sisyphus立即從山地重新出發)
現在, 宙斯有q個詢問。每個詢問ti, 他want to know, 他最少需要實戰多少個魔法才能使Sisphus到達山頂所用的年數大於ti。
題目思路
根據題目描述, 使用第i個魔法相當於多走了a[i]的路, 路越長, 時間越長, 所以根據貪心思想, 因爲要求最少使用的魔法數量, 假設長度不變, 那麼取得魔法增加的路程越長, 數量就越少, 根據這個, 我們可以排序, 然後算出前i個魔法使用後的時間,(排好序後, 取前i個一定是最優方案)。
下面, 就是取多少個的問題了。
顯然, 數列滿足單調性(因爲排序了唄), 二分即可。
詳細內容見代碼
代碼
double L, v;
double a[N];
double b[N];
long long a;
//b[i] 表示使用前i個魔法後爬上山頂所用的時間
bool cmp(int a, int b)
{return a > b;}
int main()
{
cin >> n >> L >> v;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
}
sort(a+1, a+n+1, cmp);//魔法長度從大到小排序:貪心
b[0] = L / v//t = s / v, 小學內容。。。
for(int i = 1; i <= n; i++)
{
b[i] = a[i] / v + b[i-1];//把每一塊多走得時間給我加上
}
int q;
cin >> q;
while(q--)//對於每一個詢問
{
double t, ans;
cin >> t;
if(b[mid] <= t)
{
printf("-1\n");
return 0;
}//如果所有魔法都使用了但爬上山頂的時間還是小於t的話, 無解, 輸出-1
//下面是二分。。。
int l = 0; r = n, ans = 0;
while(l <= r)
{
int mid = (l+r) >> 1;
if(b[mid] > t)//使用前mid個魔法所用的時間超過t的話
{
ans = mid;
r = mid-1;
}
else
{
l = mid + 1;
}
}
printf("%d\n", ans);
}
return 0;
}//完結撒花。。。
呵呵, 其實後面都沒有這麼詳細的代碼了。。。
B 荊軻刺秦王
題目描述
咸陽宮的地圖可以描述爲一個n行m列的矩形。 在這裏, 我們規定每一行從左到右爲x軸正方向, 每一列中從上到下y軸爲正方向,左下角點的座標爲(1,1)。矩形中的點可以分爲4種:
1.起點, 也就是荊軻所在點, 在地圖中用字符’s’代表。
2.終點, 也就是嬴政所在點, 在地圖中用字符’T’代表。
3.衛兵, 再地圖中用一個正整數代表。 在這裏,, 一個衛兵可以觀察到與他曼哈頓距離小於的點, 也就是衛兵可以觀察到所有滿足 ∣x−i∣+∣y−j∣<的點。
4.空地, 在地圖中用字符’.'代表,
荊軻的正常移動方式爲每秒向八連通的任意方向前進一格。如下圖, 中間的點爲荊軻當前所在點。每一秒, 他可以走向其餘的八個點。
需要注意的是,正常移動時, 荊軻不能踏進任何一個有衛兵或者衛兵能觀察到的格子。當然, 他也不能走出咸陽宮, 也就是說, 無論何時, 荊軻的座標(x,y)都必須滿足且
荊軻還有兩種技能:隱身和瞬移。
1.隱身:下一秒荊軻進入隱身狀態,衛兵觀察不到荊軻, 荊軻可以進入衛兵的觀察範圍內, 但仍然不能進入衛兵所在的格子。注意這個狀態只能持續一秒
2.瞬移:荊軻下一秒移動的距離改爲d, 但這時只能向上下左右四個方向移動
即可以移動到.在本題中, 兩種技能同時使用, 而且不考慮冷卻時間, 即一次用完可以立即用下一次, 兩種技能都分別有使用次數限制, 你也可以不用完所有次數。
現在給出咸陽城的地圖,請計算荊軻到達秦王所在點所需的最短時間。此外,在所用時間相同情況下,荊軻希望使用的兩種技能總次數儘可能少;在所用時間與技能次數相同情況下,荊軻希望使用的隱身次數儘可能少。
題目描述
我跪了。。。。。。。。。。。。。。。。。。。。。
一看就是搜索(對, 搜索, 做完後再也不想學搜索)
廣搜顯然是靠譜的。
狀態肯定有座標, 當前走的步數,瞬移和隱身。
結構大約是這樣的:
可能大家還有點懵, 或者說完全懵, 那麼看代碼吧。。。。細節在裏面。
代碼
struct Point
{
int step;//從起點到當前點走了多少步
int x;//橫座標
int y;//縱座標
int tl;//已經用的瞬移次數
int inv;//已經用的隱身次數
Point(){}
Point(int a, int b, int c ,int d, int e)
{
step = a;
x = b;
y = c;
inv = d;
tl = e;
}//構造函數
};
Point ans = {0x3f3f3f3f, 0, 0, 0x3f3f3f3f, 0x3f3f3f3f};//答案
int n, m, c1, c2, d;
int map[310][310];//地圖
int sx, sy, ex, ey;//終起點座標
int vis[310][310][310][310]//vis[i][j][k][l]對應x, y, inv, tl;
int bl[310][310];//被看到次數的差分數組;
void lookaround(int x, int y, int k)//衛兵所處位置(x,y)和他的值
{
//分別處理橫座標與縱座標的區間, 左+, 右減。
//枚舉到(x, y) 點的距離
for(int i=0;i<=k;i++){
bl[max(x-i,1)][max(y-(k-i),1)]++;
bl[max(x-i,1)][min(y+(k-i),m)+1]--;
bl[min(x+i,n)][max(y-(k-i),1)]++;
bl[min(x+i,n)][min(y+(k-i),m)+1]--;
}//max, min是爲了防止越界。
}
bool islook[310][310];//當前格子是否能被衛兵看見
void solve()
{
for(int i=1;i<=n;i++){
int sum=0;
for(int j=1;j<=m;j++){
sum+=bl[i][j]; //求出islook數組(前綴和)
if(sum>0)islook[i][j]=1;//有衛兵能看到這兒
}
}
}
queue<Point> q;
Point better(Point a, Point b)
{
/*請計算荊軻到達秦王所在點所需的最短時間。此外,在所用時間相同情況下,荊軻希望使用的兩種技能總次數儘可能少;在所用時間與技能次數相同情況下,荊軻希望使用的隱身次數儘可能少。*/
//根據這個, 可以翻譯出這樣一段代碼
if(a.step != b.step)
{
return a.s < b.s? a : b;
}
if(a.tl + a.inv != b.tl + b.inv)
{
return a.tl + a.inv < b.tl + b.inv ? a : b;
}
return a.inv < b.inv ? a : b;
}
const int dx[8]={1,0,-1,0,1,-1,-1,1},dy[8]={0,1,0,-1,1,1,-1,-1};//座標變化量, bfs裏用循環枚舉下一步位置。
void bfs()
{
while(!q.empty())
{
Point now = q.front();
q.pop();
if(q.step > ans.step)
{
continue;//如果當前狀態的步數已經比答案步數要大, 沒有必要搜索下去了,因爲你後來肯定還要走。
}
if(now.x == ex && now.y == ey)//如果當前狀態已經走到終點了
{
ans = better(ans,now);//答案去兩者更好的那一個。
continue;
}
for(int i = 0; i < 8; i++)//不瞬移, 八連通
{
int nx = now.x + dx[i];
int ny = now.y + dy[i];//下一步做標
if(nx<1||nx>n||ny<1||ny>m||map[nx][ny]>0)continue;//越界和有衛兵在那站着的格子都不能走到
if(islook[nx][ny])//被衛兵看到, 要用隱身
{
if(vis[nx][ny][now.inv+1][now.tl]||now.inv+1>c1)continue;//隱身次數超過上限或當前狀態已經在隊列了
vis[nx][ny][now.inv+1][now.tl] = 1;
q.push((Point){s+1,nx, ny, now.inv+1, now.tl});
}
else//不隱身
{
if(vis[nx][ny][now.inv][now.tl])
{
continue;
}
vis[nx][ny][now.inv][now.tl] = 1;
q.push((Point){s+1,nx, ny, now.inv, now,tl});
}
}
for(int i = 0; i < 4; i++)//瞬移, 只能走上下左右四個方向
{
int nx = now.x + dx[i]*d;
int ny = now.y + dy[i]*d;//一次能走d格
if(nx<1||nx>n||ny<1||ny>m||map[nx][ny]>0)continue;
if(islook[nx][ny])//還要用隱身
{
if(vis[nx][ny][now.inv+1][now.tl+1]||now.inv+1>c1 || now.tl+1>c2)
{continue;}
vis[nx][ny][now.inv+1][now.tl+1] = 1;
q.push((Point){s+1,nx, ny , now.inv+1, now.tl+1});
}
else//不用隱身
{
if(vis[nx][ny][now.inv][now.tl+1]||now.tl+1 > c2)
{
continue;
}
vis[nx][ny][now.inv][now.tl+1] = 1;
q.push((Point){s+1, nx, ny, now.inv, now.tl+1});
}
}
}
int main()
{
cin >> n >> m >> c1 >> c2 >> d;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
string s;
cin >> s;//輸入;
if(s == 'S')//是起點, 對應-1(這是自己想的對應關係, 下面也是
{
sx = i;
sy = j;
map[sx][sy] = -1;
q.push((Point){0, sx, sy, 0, 0});//bfs第一個狀態.
vis[sx][sy][0][0] = 1;//此狀態在隊列裏.
}
else if(s == 'T')//終點對應-2;
{
ex = i;
ey = j;
map[ex][ey] = -2;
}
//空地爲0;
else//衛兵。
{
int x=0;
for(int i=0;i<s.size();i++)
x=(x<<1)+(x<<3)+(s[i]^'0');//類似快讀。
map[i][j] = x;//他站的位置
lookaround(i, j, x-1);//他看到的位置。
}
}
}
solve();//求出每個格子是否被衛兵看到
bfs();//廣搜
if(ans.step = 0x3f3f3f3f)//沒被更新過
{
printf("-1\n");
}
else
{
printf("%d %d %d\n", ans.step, ans.inv, ans.tl);
}
return 0;
}
體會到了吐血的感覺qwq
C 建設城市
題目描述
球球是一位建築師。一天, 他收到市長的任務:建設城市。球球打算建造2n座高樓。爲了保證城市美觀, 球球做出瞭如下計劃:
1.球球喜歡整齊的事物, 他希望高樓從左到右排成一行, 編號依次爲1~2n。
2.他喜歡整數, 他要求每座高樓的高度都是正整數。
3.由於材料限制, 高樓的高度無法超過m。
4.球球喜歡中間高兩邊低的造型, 他要求前n座樓高度不下降, 後n座樓高度不上升。
5.球球打算選兩座編號爲x,y的高樓作爲這座城市的地標, 他認爲只有當這兩座高樓高度相等時, 纔會讓城市變得美觀。
球球把自己的想法告訴了市長。市長希望得知所有建設城市的方案數。兩種方案不同,當且僅當某座高樓的高度在兩個方案中不同。這個問題可難倒了球球。球球找到了你,希望你能幫他算出答案。由於答案可能很大,你只需要給出答案對998244353 取模後的結果。
題目思路。
首先, 因爲x和y相等, 而且這個數列最值肯定是n(由4推出)。所以我們可以分情況討論。
1.(x <= n < y)
枚舉x, y高度, 設當前爲h。則:
1~x-1的取值範圍是[1,h]。
x+1~n的取值範圍是[h, m];
n+1~y的取值範圍是[h,m];
y+1~2n的取值範圍是[1,h];
可見, 我們只要求出每一塊的方案數, 再根據分步乘法計數原理全乘起來就是最後的答案啦。
那麼怎麼求呢。
假設當前有a個數, 取值範圍裏有b個數, 那麼方案數是。。。
看這兒!!
由此可以得到每塊兒答案爲
2. (x, y 在n同側)
將(x, y)中間那部分看成一個高樓, 那麼可見答案就爲
最後取模和逆元, 你懂的。。。
代碼
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+5;
const int p=998244353;
ll ksm(ll a,ll b){ll s=1,m=a; while(b){if(b&1)s=s*m%p; m=m*m%p,b>>=1;} return s;}
ll m,n,x,y,ans,fc[N],ifc[N];
ll C(ll m,ll n){return fc[n+m-1]*ifc[n]%p*ifc[m-1]%p;}
int main(){
cin>>m>>n>>x>>y;
fc[0]=1; for(int i=1;i<=m+n;i++)fc[i]=fc[i-1]*i%p;
ifc[m+n]=ksm(fc[m+n],p-2); for(int i=m+n-1;~i;i--)ifc[i]=ifc[i+1]*(i+1)%p;
if(x<=n&&y>n)for(int i=1;i<=m;i++)ans=(ans+C(i,x-1)*C(m-i+1,n-x)%p*C(m-i+1,y-n-1)%p*C(i,2*n-y))%p;
else ans=C(m,n)*C(m,n+x-y)%p;
cout<<ans<<endl;
return 0;
}