NOI Online #2入門組

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.衛兵, 再地圖中用一個正整數ai,ja_{i,j}代表。 在這裏,, 一個衛兵(i,j)(i,j)可以觀察到與他曼哈頓距離小於ai,ja_{i,j}的點, 也就是衛兵(i,j)(i,j)可以觀察到所有滿足 ∣x−i∣+∣y−j∣<ai,jai,j的點(x,y)(x,y)
4.空地, 在地圖中用字符’.'代表,
荊軻的正常移動方式爲每秒向八連通的任意方向前進一格。如下圖, 中間的點爲荊軻當前所在點。每一秒, 他可以走向其餘的八個點。在這裏插入圖片描述
需要注意的是,正常移動時, 荊軻不能踏進任何一個有衛兵或者衛兵能觀察到的格子。當然, 他也不能走出咸陽宮, 也就是說, 無論何時, 荊軻的座標(x,y)都必須滿足1xm1 \leq x \leq m1yn1 \leq y \leq n
荊軻還有兩種技能:隱身和瞬移。
1.隱身:下一秒荊軻進入隱身狀態,衛兵觀察不到荊軻, 荊軻可以進入衛兵的觀察範圍內, 但仍然不能進入衛兵所在的格子。注意這個狀態只能持續一秒
2.瞬移:荊軻下一秒移動的距離改爲d, 但這時只能向上下左右四個方向移動
即可以移動到(x+d,y),(xd,y),(x,y+d),(x,yd)(x+d, y), (x-d, y), (x, y+d), (x, y-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個數, 那麼方案數是。。。
看這兒!!
由此可以得到每塊兒答案爲Ca+b1b1C_{a+b-1} ^ {b-1}

2. (x, y 在n同側)

將(x, y)中間那部分看成一個高樓, 那麼可見答案就爲Cn+m1m1×Cn+m+xy1m1C_{n+m-1}^{m-1} \times C_{n+m+x-y-1} ^{m-1}
最後取模和逆元, 你懂的。。。

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章