Codeforces Round 946 (Div. 3)

面包夹芝士發表於2024-05-23

C.Beautiful Triple Pairs

題意 :優美組的定義是一共三對有且只有一對對應的數字不相同,求有多少個優美三元組

思路 :對於只有三對,而且只有一對不同,首先看前面遍歷過的三元組會對後面的三元組產生影響,若是不記錄前面對後面三元組的次數,那麼我們要進行兩次迴圈 O(n^2 ) 會寄的,因此我們會想到記錄前面出現的次數。但是三元組有兩個位子相同,一個位子不同,

a = {1,2,3} ; b = {1,100,3} 。假設我們第二個位子不同,這兩個三元組出現的次數要歸到一塊去,但是有一個位子是不同的對於資料的範圍我在歸類時候選擇了將不同的位子數變成0, 即 {1,0,3} 。 因此我們可以計算前面出現的次數了。可以選擇用map存數出現的次數。

在計算位置時候記得去重,因為會出現同樣的三元組,但是這兩個三元組不是答案。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map>
using namespace std;
 
const int N = 3e5 + 10;
 
int a[N];
 // 12 13 23
 
void solve()
{
	int n; cin >> n;
	for (int i = 1; i <= n; i++)cin >> a[i];
	map<vector<int>, int> mp;
	vector<vector<int>> q;
	long long ans = 0;
	for (int i = 1; i <= n - 2; i++)
	{
		ans += mp[{a[i], 0, a[i + 2]}];
		ans -= mp[{a[i], a[i + 1], a[i + 2]}];
		ans += mp[{a[i], a[i + 1], 0}];
		ans -= mp[{a[i], a[i + 1], a[i + 2]}];
		ans += mp[{0, a[i + 1], a[i + 2]}];
		ans -= mp[{a[i], a[i + 1], a[i + 2]}];
		mp[{a[i], 0, a[i + 2]}]++;
		mp[{a[i], a[i + 1], 0}]++;
		mp[{0, a[i + 1], a[i + 2]}] ++;
		mp[{a[i], a[i + 1], a[i + 2]}]++;
	}
	cout << ans << "\n";
}
 
int main()
{
	int t = 1; cin >> t;
	while (t--)
	{
		solve();
	}
}

E. Money Buys Happiness

題意 :給你 n 個月,每個月可以用 c[i] 錢 買 h[i] 的幸福,並且每個月最多買一次。每個月可以得到 x 塊錢,但是這 x 塊錢不能在這個月花掉,只能在下個月以及下個月之後用,計算最後買到的幸福最大值。

思路 :dp[i][j] 再第 i 個月 擁有 j 幸福所花的錢是最少是多少。

注意 : 不可以直接用 1e5 來遍歷幸福對應的值,會 T 在 test 14(親身經歷)。用滾動陣列來最佳化小下。

什麼時候能轉移呢,只有當 dp[j] + v[i] <= m * (i - 1) 才可以進行轉移

轉移方程

dp[j + w[i]] = min(dp[j + w[i]],dp[j] + v[i]);

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
using LL = long long;

const int N = 1e5 + 10;

int v[N], w[N];

long long dp[N * 2]; // 第 i 個 價值 累計起來的總花費

void solve()
{
    int n;
    long long m; cin >> n >> m;
    int sum = 0;
    for (int i = 1; i <= n; i++)
        cin >> v[i] >> w[i],sum += w[i];
    for (int i = 1; i <= N; i++)
        dp[i] = 0x3f3f3f3f3f3f3f;
    dp[0] = 0;
    for (int i = 1; i <= n; i++) // 商品
    {
        for (int j = sum ; j >= 0 ; j --) 
        {
            if (m * (i - 1) >= dp[j] + v[i])
            {
                dp[j + w[i]] = min(dp[j + w[i]],dp[j] + v[i]);
            }
        }
    }
    long long ans = 0;
    for (long long j = sum ; j >= 0 ; j --)
        if (dp[j] != 0x3f3f3f3f3f3f3f)
            ans = max(ans, j);
    cout << ans << "\n";
}

int main()
{

    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    int t; cin >> t;
    while (t--)
    {
        solve();
    }
}

F. Cutting Game

題意 : 給定a * b 的範圍格子,每次可以進行一次操作砍掉前 i 行/列 或者 後 i 行/列,倆人輪班操作,最後輸出所得到有價值點的個數。

思路 :

刪除前 i 行,一個一個找太麻煩,並且還會找到許多沒用的點,因此我們可以想到找滿足所有在 x 小於當前位置的點。即對橫座標排序

但是我們又發現,當我們刪除 i 列時候,我們會發現還需要對縱座標進行排序。

所以我們要用兩個set來進行插入,一個排x,一個排y。

#include<iostream>
#include<cstring>
#include<vector>
#include<set>
using namespace std;
using LL = long long;
 
const int N = 1e5 + 10;
 
int v[N], w[N];
 
int cnt[N];
 
void solve()
{
    cnt[0] = 0; cnt[1] = 0;
    int n, m, a, b; cin >> a >> b >> n >> m;
    set<pair<int, int>> s1,s2;
    for (int i = 1; i <= n; i++)
    {
        int x, y; cin >> x >> y;
        s1.insert({ x,y });
        s2.insert({ y,x });
    }
    int u = 1, d = a, l = 1, r = b;
    int cur = 0;
    for (int i = 1; i <= m; i++)
    {
        char c; int x; cin >> c >> x;
        if (c == 'U')
        {
            while (!s1.empty() && s1.begin()->first <= u + x - 1)
            {
                auto t = *s1.begin();
                s1.erase({t});
                s2.erase({t.second,t.first});
                cnt[cur] ++;
            }
            u += x;
        }
        if (c == 'D')
        {
            while (!s1.empty() && prev(s1.end())->first + x > d)
            {
                
                auto t = *prev(s1.end());
                s1.erase(t);
                s2.erase({t.second, t.first});
                cnt[cur] ++;
            }
            d -= x;
        }
        if (c == 'L')
        {
            while(!s2.empty() && s2.begin()->first <= l + x - 1)
            {
                auto t = *s2.begin();
                s2.erase(t);
                s1.erase({ t.second, t.first });
                cnt[cur] ++;
            }
            l += x;
        }
        if (c == 'R')
        {
            while (!s2.empty() && prev(s2.end())->first > r - x)
            {
                auto t = *prev(s2.end());
                s2.erase(t);
                s1.erase({ t.second, t.first });
                cnt[cur] ++;
            }
            r -= x;
        }
        cur ^= 1;
    }
    cout << cnt[0] << " " << cnt[1] << "\n";
}
 
int main()
{
 
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    int t; cin >> t;
    while (t--)
    {
        solve();
    }
 
}

G. Money Buys Less Happiness Now

題意 :給你n個數代表每個月可以花費a[i]的錢買1點幸福值,每過個月會給你x塊錢,但是新得到得錢在當月沒法花,求最後買到的幸福值最大是多少。

思路 : 在買當前的幸福時候,如果買不起,那麼我可以在之前買的幸福中挑選出最大的,和當前的價格對比,如果當前的價格低,那麼我們可以進行反悔購買,從而買當前的幸福,並且省錢了。

維護買到幸福的代價。用優先對列來維護。

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
using namespace std;
using LL = long long;
 
const int N = 2e5 + 10;
 
int a[N];
 
void solve()
{
    int n; cin >> n;
    long long x; cin >> x;
    priority_queue<int> q;
    for (int i = 1; i <= n; i++) cin >> a[i];
    long long sum = 0;
    for (int i = 1; i <= n; i++)
    {
        if (sum >= a[i])
        {
            q.push(a[i]);
            sum -= a[i];
        }
        else
        {
            if (!q.empty() && q.top() > a[i] && q.top() + sum >= a[i])
            {
                sum += q.top() - a[i];
                q.pop();
                q.push(a[i]);
                
            }
        }
        sum += x;
    }
    cout << q.size() << "\n";
}
 
int main()
{
 
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    int t; cin >> t;
    while (t--)
    {
        solve();
    }
 
}

相關文章