Codeforces Round #625 (ABCDE)


Codeforces Round #625 (Div. 2, based on Technocup 2020 Final Round)


前言

  • 第二次打cf,又掉分了

比賽AC


A. Contest for Robots

簡明題意

  • 一共有n道題,做出第i道題可以得pip_i分。現在給出數組r[]和b[],分別表示兩個人是否做對第i題。
  • pip_i是不知道的。現在需要求出r[]這個人獲勝的情況下,pip_i的最小值。pip_i最小爲1的正整數

正文

  • 兩人共同答對的題就沒有區分度,不管設置多少分,兩人還是平局,因此兩人都答對的題設置1分就好了。
  • b答對的題r沒有答對。這樣會使得b的得分增高,想要r分數增高,只能從r答對而b沒有答對的題入手。
  • 因此要使b答對的題分數儘可能少,就全設爲1分。而r答對b每答對的題,用來中和前面那部分分數。

代碼

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<string>
using namespace std;
 
const int maxn = 110;
 
int r[maxn], b[maxn];
 
void solve()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> r[i];
	for (int i = 1; i <= n; i++)
		cin >> b[i];
 
	bool can_win = 0;
	for (int i = 1; i <= n; i++)
	{
		if (r[i] == 1 && b[i] == 0)
			can_win = 1;
	}
 
	if (!can_win)
	{
		cout << "-1";
	}
	else
	{
		int num1 = 0, num2 = 0;
		for (int i = 1; i <= n; i++)
		{
			if (r[i] == 0 && b[i] == 1)
				num2++;
			if (r[i] == 1 && b[i] == 0)
				num1++;
		}
		cout << num2 / num1 + 1;
	}
}
 
int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}

B. Journey Planning

簡明題意

  • 有n個點,每個點有個權w[i],a點走到b點,當且僅當b-a=w[b]-w[a]。現在讓你規劃一條路線,使得這條路線的權值總和最大。這條路線的點必須是單調增的,

正文

  • 一開始想了很多,怎麼都想不到。
  • 後來突然腦子靈了。直接用w[i]-i,你會發現w[i]-i相等的點可以互相走。那麼直接用一個map,統計看哪一種w[i]-i的w[i]之和最大就可以了。

代碼

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<map>
#include<string>
using namespace std;

const int maxn = 2e5 + 10;

int r[maxn], b[maxn];
map<int, long long> mp;

void solve()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int t;
		cin >> t;
		mp[i - t] += t;
	}

	long long ans = -1;
	for (auto& it : mp)
		ans = max(it.second, ans);
	cout << ans;
}

int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}

賽後補題


C. Remove Adjacent

簡明題意

  • 給一個字符串s,如果第i的字母旁邊有一個akii碼比他小1的字符,那麼可以移除第i個字符。選取合適的移除順序,問最多可以移多少個。

正文

  • 貪心,每次移除最大的能移除的字母就可以了。

代碼

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<map>
#include<string>
#include<vector>
using namespace std;

const int maxn = 2e5 + 10;

vector<char> a;

void solve()
{
	int n;
	cin >> n;
	char las_c;
	int las_num = 0;
	getchar();
	for (int i = 1; i <= n; i++)
	{
		char t;
		scanf("%c", &t);
		a.push_back(t);
	}

	int ans = 0;
	for (char i = 'z'; i >= 'a'; i--)
	{
		for (int m = 1; m <= 100 ; m++)
			for (int j = 0; j < a.size(); j++)
				if (a[j] == i)
				{
					if (j - 1 >= 0 && a[j - 1] == i - 1) {
						a.erase(a.begin() + j), ans++;
						break;
					}
					else if (j + 1 < a.size() && a[j + 1] == i - 1) {
						a.erase(a.begin() + j), ans++;
						break;
					}
				}
	}

	cout << ans;
}

int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}

D. Navigation System

簡明題意

  • 剛給一個有向圖,再給一條行駛路線(一條路徑,比如1->4->9>10)。
  • 現在有一個導航系統,每走到一個點,導航系統會規劃一次最短路。
  • 現在問你按照所給的路徑,系統最少/最多進行多少次規劃。
  • 假如145是1-5的最短路徑,那麼如果題目所給的路徑就是145,導航系統可以一次都不重新規劃。
  • 爲什麼會有最多、最少的說法呢?同上,145是1-5的最短路,但175也是一條1-5的最短路,加入身處1號節點時,系統的規劃爲175,那麼接下來按照行駛路線,會行駛到4號節點,這是系統會規劃成45,所以最終系統會重新規劃一次。而如果在1號節點時,系統的規劃不是175而是145,那麼就不需要系統重新規劃了。因此當圖不變,行駛路線也不變,重新規劃次數還是可能改變。所以有最多、最少的說法。

正文

  • 這題讀題太難了。
  • 先思考,最多、最少可能的重新規劃次數。重新規劃在什麼時候?在到達一個點時。那麼假設行駛路徑有k個點,最少0次改變,最多,除了第一點,每次都改變,那麼最多k-1次重新規劃。
  • 說上面,就是要搞清楚,重新規劃路線,是在每次到達一個點時重新規劃。而且到達一個點時,我們有幾種選擇:1.必須重新規劃 2.必須不重新規劃 3.可以重新規劃也可以不重新規劃。
  • 如果我們知道了行駛路線上每個點是以上3種選擇的哪一種,是不是就可以算出來最多/最少重新規劃次數了呢?
  • 現在來考慮以上3種情況發生的條件。
    1.必須重新規劃路線。給出的路徑爲145,而在1點時,系統的規劃路線是15,那麼走到4時,一定會重新規劃。
    2.必須不重新規劃。給出的路徑爲145,在1點時,系統的規劃也是145,那麼走到4時,只能走4-5,那麼就一定不能重新規劃。
    3.可重新規劃也可不重新規劃。給出的路徑爲1345,在1點時系統規劃是1345,那麼來到3,最短路可以是345或365,那麼系統規劃可能是這兩種,如果選擇第一種,那麼可以不重新規劃,如果選擇第二種,那麼需要重新規劃。
  • 現在來計算最少的重新規劃次數。只需要統計行駛路線上發生情況1的點的數量。怎麼統計?當新的點不在原來點的最短路上時,發生情況1.比如1375這條路,1到5最短路是147,那麼新點就是3,原來點就是1,所以說新點3不在原來點的最短路上。
  • 計算最多的重新規劃次數。統計行駛路線上發生情況3的點的數量。情況3:當走到新的點時,比如從1走到3時,存在135和145兩條最短路,那麼我們強行使在1點時選擇145,然後來到3,這樣重新規劃次數會增加。也就是說新點在原來點的最短路上且原來點還存在另一條最短路。
  • 接下來就是如何判斷新點是否在原來點的最短路上。我們直接提前算好每個點到終點的最短路(終點就是規劃路線的最後一個點),然後判斷兩個點的最短路之差是否爲1.然後判斷原來點還存在另一條最短路,那麼直接遍歷原來點的所有連接點,看有沒有點的最短路和新點的最短路相等,有的話,條件3就成立。
  • 至於怎麼提前算好每個點到終點的最短路,直接反向建圖,以終點爲起點bfs一下就可以了。

代碼

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<map>
#include<queue>
#include<string>
#include<vector>
using namespace std;

const int maxn = 2e5 + 10;

int n, m, k, a[maxn];
vector<int> g[maxn];
vector<int> g1[maxn];
int st, ed;

int dis[maxn];
void bfs()
{
	queue<int> que;
	que.push(ed);
	while (que.size())
	{
		int u = que.front();
		que.pop();

		for (auto& v : g[u])
			if (!dis[v] && v != ed)
			{
				que.push(v);
				dis[v] = dis[u] + 1;
			}
	}
}

void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		int u, v;
		scanf("%d%d", &u, &v);
		g[v].push_back(u);
		g1[u].push_back(v);
	}

	cin >> k;
	for (int i = 1; i <= k; i++)
		scanf("%d", &a[i]);
	st = a[1], ed = a[k];

	bfs();

	//最少次數
	int ans1 = 0;
	for (int i = 2; i <= k; i++)
		if (dis[a[i]] != dis[a[i - 1]] - 1)
			ans1++;

	//最多c次數
	int ans2 = 0;
	for (int i = 2; i <= k; i++)
	{
		if (dis[a[i]] != dis[a[i - 1]] - 1)
			ans2++;
		else
		{
			for (auto& v: g1[a[i - 1]])
				if (v != a[i] && dis[a[i - 1]] - 1 == dis[v])
				{
					ans2++;
					break;
				}
		}
	}

	cout << ans1 << " " << ans2;
}

int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}

E. World of Darkraft: Battle for Azathoth

簡明題意

  • 一個人要去打怪。現在又n種武器,m種盾牌,p個怪物。一個人選擇1種武器和一種盾牌,問打怪的收益最多是多少。
  • 武器有a[i],ca[i],分別表示武器的攻擊力和武器的價格。
  • 盾牌有b[i],cb[i],分別表示盾牌的防禦力和盾牌的價格。
  • 怪物有x[i],y[i],z[i],分別表示怪物的防禦力、攻擊力、擊敗時掉落的金幣。
  • 選擇一個武器和一個盾牌後,可以得到所有 防禦力<武器攻擊力,攻擊力<盾牌防禦力 的怪物的金幣。
  • 問打怪的收益最多是多少。

正文

  • 二維偏序。
  • 當盾牌確定時,顯然高攻擊力越高,盾牌能打的怪收益越多(不計武器的價格)。我們可以先把盾牌的收益設置成他的-他的價格,那麼當我們遞增這個攻擊力時,盾牌的收益就會加上一些。
  • 把盾牌列出來,再按照攻擊力從小到大枚舉武器。顯然武器的攻擊力越高,這個武器能打的怪物就越多。我們假裝有一個怪物集合,那麼當武器的高攻擊力提高了,就會有一些新的怪物加入了這個怪物集合。當有新的怪物加入怪物集合時,那麼對於每個防禦力大於加入的怪物的攻擊力的盾牌,都會獲得一些收益累加。然後在這所有的盾牌中選一個收益值最大的,減去當前武器的價格,就得到選擇某個武器的最高收入了。
  • 接下來問題就在於,當確定一個武器後,新增了一些怪物,如何給盾牌累加收益。我們可以暴力枚舉所有的盾牌,把防禦值滿足要求的累加收益。然後找最大值,複雜度不能接受。
  • 我們也可以按照防禦值二分出符合要求的盾牌,然後累加,這樣複雜度是會降低,但仍然不穩定。因爲可能跟盾牌的防禦值都很高,怪物的攻擊力都很低,這樣的話每次還是得遍歷所有的盾牌。
    -思考,我們每次是給防禦值>怪物攻擊力的盾牌累加收益。那麼這是不是像在區間加和呢? 所以可以用線段樹來維護。如果把盾牌的防禦值設置爲區間,收益設置爲值,可以寫線段樹。支持區間加以及查詢最大值即可。
  • 直接從1-1e6建線段樹,不存在盾牌的點,收益設置成功-2e18.

代碼

#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<map>
#include<queue>
#include<string>
#include<vector>
using namespace std;

const int maxn = 1e6 + 10;

int n, m, p;//武器、盾牌、怪物數
int a[(int)1e6 + 10];
int b[(int)1e6 + 10];

struct Node
{
	int l, r;
	long long max, tag;
};
Node tree[maxn * 4];

struct Mon
{
	int x, y, z;
	bool operator < (const Mon& a) const
	{
		return x < a.x;
	}
};
Mon mon[(int)2e5 + 10];

void build(int o, int l, int r)
{
	tree[o].l = l, tree[o].r = r;
	
	if (l == r)
	{
		tree[o].max = (b[l] == 0 ? -1e18 : -b[l]);
		return;
	}

	int mid = (l + r) / 2;
	build(o * 2, l, mid);
	build(o * 2 + 1, mid + 1, r);

	tree[o].max = max(tree[o * 2].max, tree[o * 2 + 1].max);

}

void spread(int o)
{
	if (tree[o].tag)
	{
		if (tree[o].l != tree[o].r)
		{
			tree[o * 2].tag += tree[o].tag;
			tree[o * 2 + 1].tag += tree[o].tag;
		}

		tree[o].max += tree[o].tag;
		tree[o].tag = 0;
	}
}

void change(int o, int l, int r, int c)
{
	spread(o);
	if (tree[o].l == l && tree[o].r == r)
	{
		tree[o].tag = c;
		spread(o);
		return;
	}

	int mid = (tree[o].l + tree[o].r) / 2;
	if (r <= mid) change(o * 2, l, r, c);
	else if (l > mid) change(o * 2 + 1, l, r, c);
	else change(o * 2, l, mid, c), change(o * 2 + 1, mid + 1, r, c);

	spread(o * 2), spread(o * 2 + 1);
	tree[o].max = max(tree[o * 2].max, tree[o * 2 + 1].max);
}

bool x0;
void solve()
{
	cin >> n >> m >> p;

	int max_dun = 0;
	for (int i = 1; i <= n; i++)
	{
		
		int x, cx;
		scanf("%d%d", &x, &cx);
		if (a[x]) a[x] = min(a[x], cx);
		else a[x] = cx;
	}
	for (int i = 1; i <= m; i++)
	{
		int x, cx;
		scanf("%d%d", &x, &cx);
		if (b[x]) b[x] = min(b[x], cx);
		else b[x] = cx;
		max_dun = max(max_dun, x);
	}
	for (int i = 1; i <= p; i++)
		scanf("%d%d%d", &mon[i].x, &mon[i].y, &mon[i].z);
	sort(mon + 1, mon + 1 + p);
	build(1, 1, max_dun);

	int l = 1;
	long long ans = -1e18;
	for (int i = 1; i <= 1e6; i++)
		if (a[i])//存在攻擊力爲i的武器
		{
			//攻擊力提升了,相應能打的怪物就會增多幾個
			for (int j = l; j <= p; j++)
				if (mon[j].x < i)//枚舉能打的怪物
				{
					if (mon[j].y + 1 <= max_dun)		
						change(1, mon[j].y + 1, max_dun, mon[j].z);//防禦力在[mon[j].y+1, 1e6]之間的盾牌都能多打一些怪物
					l++;
				}
				else break;
			ans = max(ans, tree[1].max - a[i]);
		}

	cout << ans;
}

int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}

總結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章