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;
}

posted @ 2021-09-13 13:03  forleaves  阅读(120)  评论(0)    收藏  举报