Educational Codeforces Round 75 Editorial

爷回来啦!

2更

F要用fft或ntt,慌了


E. Voting (Easy/Hard Version) (2s 256Mb)

题目大意

你需要让n(2e5)个人全部给你投票。对于每个人i,要么花p_i的钱让他为你投票,要么如果已经有m_i个人为你投票了,他会免费为你投票,也就是说投票的过程是有渐进的。求让全部为你投票的最小花费。

分析

将m相等的人归为一类,并按照m值从大到小考虑(降序很重要)。

简单证明:对于最大m,假如将剩下的m比它小的都买了也不够最大m的话,必须从这最大m里买一些人,否则可以缩小问题范围为除去是最大m的人的问题。既然要买不如先买,然后也可以缩小问题范围。之后第二大m,第三大m。。。

注意之前的缩小后去除的人也可以买,计算够不够当前m时也要将这些提前买的m加上。

被题解带着用了multiset,其实也可以用优先队列的。

代码

int main()
{
	int T = read();
	while(T--)
	{
		n = read();
		for(int i = 1; i <= n; i++) 
		{
			a[i].m = read(), a[i].p = read();
		}
		sort(a+1, a+n+1, cmp);
		int l = 1, r, cnt = 0;
		long long ans = 0;
		while(l <= n)
		{
			r = l; while(r < n && a[r+1].m == a[l].m) r++;
			for(int i = l; i <= r; i++) s.insert(a[i]);
			if(n-r+cnt < a[l].m)
			{
				int cha = a[l].m - (n-r+cnt);
				cnt += cha;
				while(cha--)
				{
					ans += (long long) (*s.begin()).p;
					s.erase(s.begin());
				}
			}
			l = r+1;
		}
		printf("%I64d\n", ans);
		if(T) s.erase(s.begin(), s.end());
	}
	return 0;
}

D. Salary Changing (3s 256Mb)

题目大意

给n(2e5且为奇数)个人发工资,一共有s(2e14)块钱。每个人给的钱必须在 \left [ l_i,r_i \right ] (1e9)之间。使给的n个钱数中位数最大,求中位数最大可以是多少。

分析

一开始考虑了二分但是觉得不满足全域的单调性。实际上考虑了L和R的初值以后就满足。

check部分,对于每个中位数,分三种人。

  1. r_i <mid,这种人只能作为中位数前面,给他最少的 l_i 即可。
  2. mid\leq l_i,这种人只能作为中位数后面,给他最少的 l_i
  3. l_i < mid \leq r_i,这种人既可以做前面也可以做后面,每个人要么给 l_i 要么给 mid ,根据 l_i 排序即可。

最后钱不够用或者前两种人每种太多都不行。

代码

const int maxn = 2e5+10;
pair<ll, ll> a[maxn];
ll s;
int n, vis[maxn];

int check(long long mid)
{
	ll tmp = 0;
	int c1 = 0, c2 = 0;
	for(int i = 1; i <= n; i++) vis[i] = 0;
	for(int i = 1; i <= n; i++) if(a[i].second < mid) 
	{
		vis[i] = 1; tmp += a[i].first; c1++;
	}
	//if(c1 > n/2) return 0;
	for(int i = 1; i <= n; i++) if(!vis[i] && a[i].first >= mid)
	{
		vis[i] = 1; tmp += a[i].first; c2++;
	}
	for(int i = 1; i <= n && c1 != n/2; i++) if(!vis[i])
	{
		vis[i] = 1; tmp += a[i].first; c1++;
	}
	for(int i = 1; i <= n; i++) if(!vis[i])
	{
		tmp += mid; c2++;
	}
	return tmp <= s && c1 == n/2 && c2 == n/2+1;
}

int main()
{
	int T = read();
	while(T--)
	{
		n = read(); s = read();
		for(int i = 1, u, v; i <= n; i++)
		{
			u = read(), v = read();
			a[i] = {u, v};
		}
		sort(a+1, a+n+1);
		ll l = a[n/2+1].first, r = s;
		while(l != r)
		{
			ll mid = l + ((r-l)>>1) + 1;
			if(check(mid)) l = mid;
			else r = mid-1;
		}
		printf("%I64d\n", l);
	}
	return 0;
}

C. Minimize The Integer (2s 256Mb)

题目大意

给你n(3e5)长0-9串,如果相邻digit一奇数一偶数则可以交换位置(同为奇数或同为偶数不行)。求可以得到的字典序最小串。

分析

分析性质。由题意,奇数字串和偶数字串各自顺序不会变(必要性),满足这一性质的串可以任意构造(充分性),随便贪(归并)。

代码

const int maxn = 3e5+10;
int n, a[maxn], c[maxn], d[maxn]; char s[maxn];
int main()
{
	int T = read();
	while(T--)
	{
		scanf("%s", s+1);
		int n = strlen(s+1);
		for(int i = 1; i <= n; i++) a[i] = s[i]-'0';
		int tot1 = 0, tot2 = 0;
		for(int i = 1; i <= n; i++) 
		{
			if(a[i]&1) c[++tot1] = a[i];
			else d[++tot2] = a[i];
		}
		int x = 1, y = 1;
		while(x <= tot1 && y <= tot2) 
		{
			if(c[x] < d[y]) putchar(c[x++]+'0');
			else putchar(d[y++]+'0');
		}
		while(x <= tot1) putchar(c[x++]+'0');
		while(y <= tot2) putchar(d[y++]+'0');
		putchar(10);
	}
	return 0;
}

B. Binary Palindromes (2s 256Mb)

题目大意

给你n(50)个01串s_i\left|s_i\right|\leq50,可以交换两个bit的位置任意次(可以不同串),问最多能构成多少回文串。

分析

有点意思。一开始想复杂了。任意交换就可以看0和1总个数,但每个串长度固定。先把0和1都拿出来,分给每个串。

对于奇数长串,该串无论0和1有多少都能安排成回文(0和1的个数必然一奇数一偶数)。对于偶数长串,如果0和1的个数是偶数、偶数,可以安排成回文。如果奇数、奇数,则不可以(违反必要性)。

考虑一下的话答案要么是n要么是n-1。

对于偶,不考虑,因为使用了0和1后,对0和1总数奇偶性无影响。对于奇数串,不考虑最中间位置就成偶数串(最中间填啥都行)。

  1. 如果0和1个数全是偶数,随便填。
  2. 如果奇数、偶数,至少一个奇数串。拿一个0或1出来放在奇数串最中间,去构成偶数、偶数,随便填。
  3. 如果奇数,奇数,拿一个0、一个1,去构成偶数、偶数。如果奇数串大于等于2,拿出来的可以放在最中间处理;否则只能废掉一个串。

所以只有一种情况答案是n-1。

if(b[0]&1 == 1 && b[1]&1 == 1 && tmp < 2) ans = n-1;
//0,1个数都是奇数,且没有奇数串(tmp为偶)

代码

const int maxn = 60;
int n, a[maxn];

int main()
{
	int T = read();
	while(T--)
	{
		n = read();
		int b[2] = {0, 0}, tmp = 0;
		for(int i = 1; i <= n; i++)
		{
			char c; int tot = 0;
			while(c = getchar())
			{
				if(c == '\n') break;
				tot++;
				b[c-'0']++;
			}
			tmp += tot&1;
		}
		int ans;
		if(b[0]&1 == 1 && b[1]&1 == 1 && tmp < 2) ans = n-1;
		else ans = n; 
		printf("%d\n", ans);
	}
	return 0;
}

A. Broken Keyboard (1s 256Mb)

题目大意

一个坏的打字机,某些字母按键按一次输出两个。给你一个输出文本,判断哪些按键一定是好的。

分析

一开始被带进去了,实际上坏的键只能按出偶数个连续的相同字母,好的可奇可偶。所以有奇数个连续的一定是好的。

代码

const int maxn = 1e3+10;
char s[maxn];
int vis[30];
 
int main()
{
	int T = read();
	while(T--)
	{
		memset(vis, 0, sizeof(vis));
		scanf("%s", s+1);
		int len = strlen(s+1);
		int x = 1;
		while(x <= len)
		{
			int y = x;
			while(y <= len && s[y] == s[x]) y++;
			if((y-x)&1) vis[s[x]-'a'+1] = 1;
			x = y;
		}
		for(int i = 1; i <= 26; i++) if(vis[i]) putchar('a'+i-1);
		putchar(10);
	}
	
	return 0;
}
发布了18 篇原创文章 · 获赞 0 · 访问量 1138

猜你喜欢

转载自blog.csdn.net/DWAE86/article/details/103158024