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

 

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