Educational Codeforces Round 109 (Rated for Div. 2)

比賽地址c++

A(水題)

題目連接
數組

題目:
每次能夠倒入1份藥劑/水,請問最少多少spa

解析:
直接對濃度進行進行最簡約分,輸出分母便可code

#include<bits/stdc++.h>
using namespace std;

int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }

int main() {
	int T, n;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		printf("%d\n", 100 / gcd(100, n));
	}
}

B(水題)

題目連接
排序

題目:
給出\(1\sim n\)的排序,每次能夠對子連續序列進行排序嗎,問最多進行多少次操做之後可使得數列有序get

解析:
考慮到每次對\(n-1\)個序列進行排序的狀況下(假設是對前\(n-1\)個數進行排序),若是\(1\)不在第\(n\)個位置,則下一次對後\(n-1\)個元素進行排序便可,同理若是\(n\)不在第1個位置,對後\(n-1\)個序列也能夠達到相同的目的。可是當1在第\(n\)個位置且\(n\)在第\(1\)個位置時,必須先將\(1\)\(n\)先置換出來,再進行如上步驟數學

#include<bits/stdc++.h>
using namespace std;

const int maxn = 55;
int dat[maxn];

int main() {
	int T, n;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		bool ok = true;
		for (int i = 0; i < n; ++i) {
			scanf("%d", &dat[i]);
			if (i && dat[i] < dat[i - 1]) ok = false;
		}
		if (ok) printf("0\n");
		else {
			if (dat[0] == n && dat[n - 1] == 1) printf("3\n");
			else if (dat[0] == 1 || dat[n - 1] == n) printf("1\n");
			else printf("2\n");
		}
	}
}

C(思惟+棧)

題目連接
⭐⭐⭐it

題目:
在一個X軸區間[0,m]上,給出\(n\)個機器人的初始朝向以及位置,機器人在整數點相遇時會碰撞,兩機器人均會消失,已知機器人每一個單位時間行進一個單位長度,碰到區間端點時會反向前行,求出每一個機器人撞毀的時間class

解析:test

  1. 不難發現,奇數位上的機器人必定不會與偶數位上的機器人相撞,這樣就能夠把機器人分紅奇偶兩類
  2. 對於任意一類機器人,能夠考慮將向右走的機器人不斷入棧,向左走的機器人與棧頂機器人相撞,時間很明顯是兩者之差除以二
  3. 但在棧空的狀況下,向左走的機器人至關於從\(-x\)的位置向右移動,所以能夠將\(-x\)入棧
  4. 一樣思考若是掃描到最後棧內元素有多時,能夠將向右走的機器人一樣視爲從\(2\times m-x\)的位置向左移,與次棧頂的元素相撞
  5. 若是最後棧內元素剩\(1\),則表明此機器人永遠沒法被撞毀,賦予\(-1\)
#include<bits/stdc++.h>

using namespace std;


struct TT {
	int pos, id;
	bool dir;
};
stack<TT> s;
vector<TT> v[2];
const int maxn = 3e5 + 5;
int pos[maxn], n, m;

void solve(vector<TT>& v) {
	sort(v.begin(), v.end(), [](TT i, TT j) {return i.pos < j.pos; });
	for (auto& i : v) {
		if (i.dir)
			s.push(i);
		else {
			if (!s.empty()) {
				auto t = s.top(); s.pop();
				pos[i.id] = pos[t.id] = (i.pos - t.pos) / 2;
			}
			else s.push(TT{ -i.pos,i.id,1 });
		}
	}
	while (s.size() > 1) {
		auto t1 = s.top(); s.pop();
		auto t2 = s.top(); s.pop();
		pos[t1.id] = pos[t2.id] = (2 * m - t1.pos - t2.pos) / 2;
	}
	if (!s.empty()) {
		pos[s.top().id] = -1;
		s.pop();
	}
}

int main() {
	int T;
	char ch;
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &m);
		v[0].clear(), v[1].clear();
		for (int i = 0; i < n; ++i)
			scanf("%d", &pos[i]);
		for (int i = 0; i < n; ++i) {
			scanf(" %c", &ch);
			v[pos[i] & 1].push_back(TT{ pos[i],i, ch == 'R' });
		}
		solve(v[1]), solve(v[0]);
		for (int i = 0; i < n; ++i)
			printf("%d ", pos[i]);
		printf("\n");
	}
}

D(dp)

題目連接
⭐⭐⭐

題目:
假設\(n\)個位置上至多有\(\lfloor\frac{n}{2}\rfloor\)上坐了人,用\(1\)表示,空座位用\(0\)表示,第\(i\)我的若是想要移動到第\(j\)個位置,則須要付出\(\mid i-j\mid\)的代價,現需另全部人不在原被佔有位的最小代價

解析:

  1. 首先想到的作法是最小費用流,可是邊大概有\(10^7\)條,會無情TLE,所以考慮DP的作法。
  2. 首先證實一個引理——若是要知足最小代價,則第\(i\)個空位置必定坐的\(1\sim i\)號人。假如第\(j_{mx}(j_{mx}>i)\)號人坐上了第\(i\)號位置,那麼必然存在一個\(j_{mn}\)坐上了某個大於\(i\)的位置\(k\),此時代價爲\((j_{mx}-i)+(k-j_{mn})\),可是若是兩者位置互換,則代價爲\((i-j_{mn})+(j_{mx}-k)\),兩者進行比較,不難發現左邊恆大於右邊,故引理成立
  3. 那麼考慮用狀態\(dp[i][j]\)表明前\(j\)我的坐在前\(i\)個空位時的最小代價,考慮第\(j\)我的是否坐第\(i\)個空位,不可貴到狀態轉移方程

\[dp[i][j]=\max(dp[i][j-1],dp[i-1][j-1]+|p_{empty}[j]-p_{occupied}[i]|) \]

注意:
引理的做用主要用於進行迭代更新\(dp\)數組時,對於第\(i\)個空位只須要迭代\(\min(人的個數,i)\)
若是缺乏引理,對結果其實沒有影響,可是狀態的定義會變得較爲複雜與難以理解,較難尋找到正確的狀態轉移方程

#include<bits/stdc++.h>

using namespace std;

const int maxn = 5e3 + 5;
int p[maxn];
vector<int> n1(1), n2(1);
int dp[maxn][maxn];

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 0; i < n; ++i) {
		scanf("%d", &p[i]);
		if (p[i]) n2.push_back(i);
		else n1.push_back(i);
	}
	for (int i = 0; i < n1.size(); ++i)
		for (int j = i + 1; j < n2.size(); ++j)
			dp[i][j] = 0x3f3f3f3f;
	for (int i = 1; i < n1.size(); ++i)
		for (int j = 1; j <= min(int(n2.size() - 1), i); ++j)
			dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1] + abs(n1[i] - n2[j]));
	printf("%d", n2.size() == 1 ? 0 : dp[n1.size() - 1][n2.size() - 1]);
}

E(思惟+指望+組合數學)

題目連接
⭐⭐⭐⭐

題目:
給出\(n\)個城市以及\(m\)塊區域,以及每座城市距離每塊區域之間的距離,能夠在第\(i(1\sim n)\)輪在某個城市內安插一個控制裝置,可以掌控距離該城市距離在\(i\)之內的城池,問掌控城池的指望,答案模取\(998244353\)

解析:

  1. 對於答案——掌握城池和的指望,能夠轉化爲每一個城池被掌握的指望和,也就至關於每一個城池被掌握的機率和
  2. 考慮第\(i\)個城市被掌握的機率,正向思考因爲可能出現城市重疊控制區域的狀況,難以輕鬆得到結果,所以能夠反向思考,用\(1-區域不被控制的機率\)
  3. 而對於\(n!\)種安插控制裝置的順序下,能夠反向迭代統計城市不被掌握時,安插控制裝置的順序。對於第\(i\)輪,能夠選擇全部距離大於\(i\)的城市中的一個。這個城市的數量能夠進行動態的維護
#include<bits/stdc++.h>
using namespace std;

int d[25][50005];
typedef long long ll;
const ll mod = 998244353;

int cnt[25];

ll q_pow(ll x, ll n) {
	ll ans = 1;
	while (n) {
		if (n & 1) ans = ans * x % mod;
		n >>= 1;
		x = x * x % mod;
	}
	return ans;
}

int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < m; ++j)
			scanf("%d", &d[i][j]);
	ll inv = 1;
	for (int i = 2; i <= n; ++i)
		inv = inv * i % mod;
	inv = q_pow(inv, mod - 2);
	ll ans = 0;
	for (int j = 0; j < m; ++j) {
		memset(cnt, 0, sizeof(cnt));
		for (int i = 0; i < n; ++i)
			++cnt[d[i][j]];
		ll ret = 1, now = 0;
		for (int i = n; i >= 1; --i) {
			now += cnt[i + 1];
			ret = ret * now % mod;
			if (now-- == 0) break;
		}
		ans = ((ans + 1 - ret * inv % mod) % mod + mod) % mod;
	}
	printf("%lld", ans);
}
相關文章
相關標籤/搜索