1. 程式人生 > 其它 >Codeforces Global Round 16(A~E)

Codeforces Global Round 16(A~E)

A. Median Maximization

題意:限制n個數,n個數的和為s,每個數必須是非負整數,詢問中位數的最大值,n為偶數時中位數取前者

分析:對於小於中位數的值直接定為0,後面的數直接分配就好

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 3e5 + 10, mod = 1e9 + 7;

int n, m, t, k;

int s[N], dp[N], f[N];
char g[N];

string str;

vector<int> vec;

void solve()
{
	cin >> n >> m;
	printf("%d\n", m / (n/2 + 1));
}

int main()
{
    t = 1;
    cin >> t;
    while(t--) 
        solve();
    return 0;
}

B. MIN-MEX Cut

題意:給定01字串,可以隨意切分,每一段的值為這一段沒出現過的最小非負數,詢問所有段的值的和最小

分析:檢視連續0的區間數,因為但凡範圍帶了0,那麼最小一定是1,所以全劃分答案為連續0的區間數,全選最差為2,但凡0和1連上這一段答案就為2,所以對於連續0的區間數,再與2取個最小值就好

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 3e5 + 10, mod = 1e9 + 7;

int n, m, t, k;

int s[N], dp[N], f[N];
char g[N];

string str;

vector<int> vec;

void solve()
{
	cin >> str;
	int b = str[0] == '0';
	for (int i = 1; i < str.size(); i++)
		if (str[i] == '0' && str[i - 1] == '1')
			b++;
	printf("%d\n", b < 2 ? b : 2);
}

int main()
{
    t = 1;
    cin >> t;
    while(t--) 
        solve();
    return 0;
}

C. MAX-MEX Cut

題意:有兩個長度相同的字串,可以分成任意段,兩個字串相同劃分,每一段的值為這一段沒出現過的最小非負數,詢問所有段的值的和最大

分析:如果當列包含0和1,那麼這一段單獨切開絕不會差,否則全0值是1,全1值是0,只有這兩段連起來會比不加起來多1,並且如果加起來就一定會把這兩段連起來跟其他的直接切開,所以先把所有段單獨計算值,對每一段的值疊加,假設當前進行到第i位,而第i位和第i-1位的值剛好是一個0和一個1,就可以考慮將這兩段連續,到第i位的最大值就是f[i-2]+2,過一遍n即可,f陣列是存放前i位最大和的

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 3e5 + 10, mod = 1e9 + 7;

int n, m, t, k;

int s[N], dp[N], f[N];
char g[N];

string str, str1;

vector<int> vec;

void solve()
{
	cin >> n;
	cin >> str >> str1;
	for (int i = 0; i < n; i++)
	{
		if (str[i] == '0' && str1[i] == '0') s[i+1] = 1;
		if (str[i] == '0' && str1[i] == '1') s[i+1] = 2;
		if (str[i] == '1' && str1[i] == '0') s[i+1] = 2;
		if (str[i] == '1' && str1[i] == '1') s[i+1] = 0;
	}
	for (int i = 1; i <= n; i++)
	{
		f[i] = f[i-1] + s[i];
		if (i > 1 && s[i] + s[i-1] == 1)
			f[i] = f[i-2] + 2;
	}
	cout << f[n] << endl;
}

int main()
{
    t = 1;
    cin >> t;
    while(t--) 
        solve();
    return 0;
}


D1. Seating Arrangements (easy version)

題意:有n列m行,要求從1到nm的值必須是非遞減序列,此版本n為1,所有人進入都是從km+1的位置進入到自己的位置,在進入到自己位置的時候,越過了幾個人,答案就會加幾,詢問,答案的最小值

分析:因為n為1,所以不考慮n,對於m所有人的位置當大小嚴格不等的時候,沒選擇,當大小相等的時候,先來人的進入的位置顯然會更靠裡,更深,所以排個序,列舉即可,m範圍300,我當成1e5樹狀陣列做的,排序規則為第一維小的考前,第二位大的靠前,存位置時可以考慮存相反數

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 3e5 + 10, mod = 1e9 + 7;

int n, m, t, k;

int s[N], dp[N], f[N];
char g[N];

string str, str1;

vector<int> vec;

struct node{
	int a, b;
}tr[N];

bool cmp(node a, node b)
{
	if (a.a == b.a)
		return a.b > b.b;
	return a.a < b.a;
}

int lowbit(int x)
{
    return x & -x;
}

void add(int x)
{
    for (int i = x; i <= m; i += lowbit(i)) dp[i]++;
}

int sum(int x)
{
    ll res = 0;
    for (int i = x; i; i -= lowbit(i)) res += dp[i];
    return res;
}

PII p[N];

void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		dp[i] = 0;
		cin >> s[i];
		tr[i] = {s[i], i};
	}
	sort(tr + 1, tr + m + 1, cmp);
	for (int i = 1; i <= m; i++)
		s[tr[i].b] = i;
	ll ans = 0;
	for (int i = 1; i <= m; i++)
	{
		ans += sum(s[i]);
		add(s[i]);
	}
	cout << ans << endl;
}

int main()
{
    t = 1;
    cin >> t;
    while(t--) 
        solve();
    return 0;
}

D2. Seating Arrangements (hard version)

題意:同D1,不同的只有n<=300

分析:因為嚴格要求對於1到n*m的值,必須非遞減,所以其實每個人的位置都是基本固定的,唯一不同的是,因為n大於1了,所以有可能連續的一段會變成一列的最後幾個位置連上下一列的前幾個位置,這種時候,顯然是需要深度排序,先來的先做後面的位置,這樣答案一定不會更差,所以首先還是和D1一樣,先排個序,map屬於對映,用map構造對應值與vector的對映關係,就可以直接提前排好序,便利map,對於每一個vector內部的座標,都是固定的,只有同一個vector內部的人可以調換,這裡用令一個vector記錄下當前一批的vector的座標,然後按深度排序,定位,就可以知道對於每一個位置要去的是第幾個人,之後\(n^3\)查詢即可

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 1e6 + 10, mod = 1e9 + 7;

int n, m, t, k;

int f[N];

map<int,vector<int> > ma;

int s[310][310];

void solve()
{
	scanf("%d%d", &n, &m);
	ma.clear();
	for (int i = 1; i <= n * m; i++)
	{
		scanf("%d", &f[i]);
		ma[f[i]].push_back(i);
	}
	int a = 1;
	int b = 0;
	for (auto h : ma)
	{
		vector<int> vec1 = h.second;
		vector<PII> vec2;
		for (int i : vec1)
		{
			b++;
			if (b == m + 1)
			{
				b = 1;
				a++;
			}
			vec2.push_back({a, -b});
		}
		sort(vec2.begin(), vec2.end());
		for (int i = 0; i < (int)vec2.size(); i++)
			s[vec2[i].first][-vec2[i].second] = vec1[i];
	}
	int ans = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			for (int l = 1; l < j; l++)
				if (s[i][l] < s[i][j])
					ans++;
	printf("%d\n", ans);
}

int main()
{
    t = 1;
    cin >> t;
    while(t--) 
        solve();
    return 0;
}

E. Buds Re-hanging

題意:給出一棵樹,根節點為1,沒有子節點的稱之為葉子,不為根且有子節點的並且所有子節點都是葉子的成為芽,我們可以對任意芽進行任意次操作,操作:將芽連線著他的所有子節點重新懸掛到該樹的任一點上,問如此操作後,該樹的葉子最小為多少。

分析:顯然對於每一次操作,如果我們進行的話,我們都會把這個芽連線到該樹的葉子上去,這會是使葉子數減1,移動完後對於原樹的這個枝葉來說就沒了,該結點就會移走,返回0,否則該結點為葉子,返回1,該節點為葉子的話,意味著父節點可以帶著移動走,假設移動了x次,答案會累加x-1次,因為最終一定會有一個枝葉不移動,別的芽都移動過這裡,這些算的都是移動後增加的葉子,不管怎麼移動,最後的枝葉原本一定會有一個葉子存在的,所以初始化為1,答案dfs疊加即可

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 1e6 + 10, mod = 1e9 + 7;

int n, m, t, k, ans;

vector<int> vec[N];

int dfs(int u, int v)
{
	int cnt = 0;
	for (auto i : vec[u])
	{
		if (i == v) continue;
		cnt += dfs(i, u);
	}
	if (cnt)
	{
		ans += cnt - 1;
		return 0;
	}
	return 1;
}

void solve()
{
	cin >> n;
	for (int i = 1; i <= n; i++) vec[i].clear();
	for (int i = 1; i < n; i++)
	{
		int a, b;
		cin >> a >> b;
		vec[a].push_back(b);
		vec[b].push_back(a);
	}
	ans = 1;
	dfs(1, 0);
	printf("%d\n", ans);
}

int main()
{
    t = 1;
    cin >> t;
    while(t--) 
        solve();
    return 0;
}