CF Round946(div3) 題解

zerocloud01發表於2024-05-30

目錄
  • 946(div3)
    • A
    • B
    • C
    • D
    • E
    • G

946(div3)

A

每個螢幕3X5, 可放2個2X2, 其餘可填7個1X1 先算2X2需要多少個螢幕,再算當前螢幕是否可放下所有1X1, 根據1X1的量加螢幕.

void func()
{
	int a,b;
	cin >> a >> b;
	int ans = (b+1) / 2;
	int stp = a - (ans*15 - 4*b);
	if(stp > 0)	ans += (stp + 14) / 15;
    //當前螢幕是否可以放下所有a,不能則再加螢幕. 
	cout << ans << '\n';
}

B

s為原字串,r為s中字字母去重排序後字串串. 題目需要求將s中每個字元根據r替換為r中對稱字元後的字串的原字串. 實際操作可逆, 從s'得到s和從s得到s'的操作是一樣的.
用s將r處理出來,再把對稱字元互相對映用二分硬找也行,也是O(nlogn),輸出替換的串即可.

void func()
{
	int n;
	string st;
	cin >> n >> st;
	vector<char> a(n);
	for(int i=0;i<n;++i)	a[i] = st[i];
	sort(a.begin(),a.end());//處理r
	a.erase(unique(a.begin(),a.end()),a.end());//去重函式
	int len = a.size();
	map<char,char> mp;//對稱串對映
	for(int i=0;i<len;++i)
	{
		mp[a[i]] = a[len - 1 - i];
	}
	for(int i=0;i<n;++i)
	{
		st[i] = mp[st[i]];
	}
	cout << st << '\n';
}

C

資料最多可有1011, 要開longlong
解法1: 3map暴力O(nlogn)
找到連續三個字元的字串中有一個不同的個數. 時間長且資料不大,開三個map分別存123號位不同的點即可. 統計其中兩個位相同第三個位相同和不同的數目,相乘加入總數即可.
程式碼比較醜陋, 直接貼上了三個for().

void func()
{
	int n;
	cin >> n;
	vector<int> v(n);
	map<pair<int,int>,vector<int>> mp1,mp2,mp3;//三個二元組對映vector, 根據其中兩位對映第三位. 
	int a,b,c;
	for(auto &i : v) cin >> i;
	for(int i=0;i<n-2;++i)
	{
		mp1[{v[i],v[i+1]}].push_back(v[i+2]);// 儲存
		mp2[{v[i],v[i+2]}].push_back(v[i+1]);
		mp3[{v[i+1],v[i+2]}].push_back(v[i]);
	}
	int cnt = 1,ans = 0;
	for(auto &i : mp1)//每次處理一組兩位置相同的元素
	{
		sort(i.y.begin(),i.y.end());//排序,方便根據前一位統計有幾個相同
		int size = i.y.size();
		for(int j=1;j<size;++j)
		{
			if(i.y[j] == i.y[j-1])	cnt++;
			else
			{
				ans += (size-cnt)*cnt;
				cnt = 1;
			}
		}
		ans += (size-cnt)*cnt;//可能最後一元素與前一個相同, 需單獨處理
		cnt = 1;
	}
	//後二for()操作相同
	for(auto &i : mp2)
	{
		sort(i.y.begin(),i.y.end());
		int size = i.y.size();
		for(int j=1;j<size;++j)
		{
			if(i.y[j] == i.y[j-1])	cnt++;
			else
			{
				ans += (size-cnt)*cnt;
				cnt = 1;
			}
		}
		ans += (size-cnt)*cnt;
		cnt = 1;
	}
	for(auto &i : mp3)
	{
		sort(i.y.begin(),i.y.end());
		int size = i.y.size();
		for(int j=1;j<size;++j)
		{
			if(i.y[j] == i.y[j-1])	cnt++;
			else
			{
				ans += (size-cnt)*cnt;
				cnt = 1;
			}
		}
		ans += (size-cnt)*cnt;
		cnt = 1;
	}
	cout << ans/2 << '\n';
}

解法2: 容斥O(nlogn)
統計出12,13,23號位相同的可能性, 再減去123號位完全相同的可能.
注意: 在12,13,23三組,統計了三次123相同,所以最後結果需要減三個123.
容斥原理

void func()
{
	int n,ans1 = 0,ans2 = 0;
	cin >> n;
	map<pair<int,int>,int> cnt1,cnt2,cnt3;//分別存12, 13, 23相同的元素數目
	map<edge,int> cnt4;// 存三個元素完全相同的元素數目
	vector<int> v(n);
	for(int i=0;i<n;++i)	cin >> v[i];
	for(int i=0;i<n-2;++i)
	{
		cnt1[{v[i],v[i+1]}]++;
		cnt2[{v[i],v[i+2]}]++;
		cnt3[{v[i+1],v[i+2]}]++;
		cnt4[{v[i],v[i+1],v[i+2]}]++;
	}
	for(auto &i : cnt1)	ans1 += (i.y-1)*i.y/2;
	for(auto &i : cnt2)	ans1 += (i.y-1)*i.y/2;
	for(auto &i : cnt3)	ans1 += (i.y-1)*i.y/2;
	for(auto &i : cnt4)	ans2 += i.y*(i.y-1)/2;
	int ans = (ans1 - 3*ans2);
	cout << ans << '\n';
}

D

保證兩者都進行過運動下直接暴力即可, 因為多個if太長了, 抄了會長的程式碼.
對於每種操作, 第一次給了H, 第二次就必須給R. 所以計算同類操作MOD2後是否相同來確定此方向下HR同位置. 另外, 將Y軸操作初試設為1, X軸操作設為0可以保證有解情況下兩者一定進行了操作.

void func()
{
	int n;
	string st,ans;
	cin >> n >> st;
	int cnt = 0;// 確定都進行操作
	map<char,int> mp;
	mp['W'] = mp['E'] = 1;
	mp['N'] = mp['S'] = 0;
	for(int i=0;i<n;++i)
	{
		ans += (mp[st[i]] == 0 ? 'R' : 'H');
		if(ans[i] == 'R')	cnt |= 1;// R
		if(ans[i] == 'H')	cnt |= 2;// H
		mp[st[i]] ^= 1;
	}
	/*
	cnt | 1 | 2 == 3, 保證兩者都進行操作
	N == S, W == E, 保證位置相同
	*/
	if(cnt == 3 && mp['N'] == mp['S'] && mp['W'] == mp['E'])	cout << ans << '\n';
	else	cout << "NO\n";
}

E

G

貪心, 如果當前金錢可以購買本月快樂, 則加入答案, 但是如果買本次快樂的錢足夠買後面兩次的, 那就不成立, 所以在遇到後續更便宜的快樂時替換掉, 總快樂不變但是餘額變多.
其實是反悔貪心, 把每次加入的答案加入堆, 如果餘額足夠則加入, 不足則和堆最大值比較, 如果更小則替換最大值, 獲得差值的金錢.

void func()
{
	int m,x;
	cin >> m >> x;
	vector<int> a(m);
	for(auto &i :a)	cin >> i;
	priority_queue<int> pq;// 大根堆
	int money = 0,happy = 0;
	for(int i=0;i<m;++i)
	{
		if(money >= a[i])
		{
			happy++;
			money -= a[i];
			pq.push(a[i]);
		}
		else if(pq.size() && pq.top() > a[i])// 注意堆為空時不能訪問. 
		{
			money += (pq.top()-a[i]);
			pq.pop();
			pq.push(a[i]);
		}
		money += x;
	}
	cout << happy << '\n';
}