2022年CCPC绵阳站 补题记录

Problem A. Ban or Pick, What’s the Trick(记忆化dp)

  • 首先,贪心地想,一定是拿自己最大值 or ban对方最大值
  • dp[u][i][j] 表示到第 u 轮,先手选了 i 个,后手选了 j 个的答案
  • 因此到了第 u 轮,先手已经拿到了 nowa = i + (u / 2 - j) 个,后手已经拿到了 nowb = j + ((u + 1) / 2 - i)
  • 如果 u 是偶数,表示当前轮为先手操作,转移方程:res = max(res, dfs(u + 1, i + 1, j) + a[nowa + 1], dfs(u + 1, i, j)) ,分别表示选自己 or ban对方
  • 如果 u 是奇数,表示当前轮为后手操作,转移方程:res = max(res, dfs(u + 1, i, j), dfs(u + 1, i, j + 1) - b[nowb + 1]),分别表示ban对方 or 选自己
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
#include <iomanip>
#include<random>
#include<set>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int f[N][15][15];
int n , k;
int a[N] , b[N];

int dfs(int u , int i , int j){
	if(u == 2 * n)return 0;
	if(f[u][i][j] != -1e18)return f[u][i][j];
	int res;
	int nowa = i + (u / 2 - j);
	int nowb = j + ((u + 1) / 2 - i);
	if(u % 2 == 0){
		res = -1e18;
		if(nowa + nowb <= 2 * n && i < k)
			res = max({res , dfs(u + 1 , i + 1 , j) + a[nowa + 1] , dfs(u + 1 , i , j)});
		else res = max(res , dfs(u + 1 , i , j));
	}else{
		res = 1e18;
		if(nowa + nowb <= 2 * n && j < k)
			res = min({res , dfs(u + 1 , i , j) , dfs(u + 1 , i , j + 1) - b[nowb + 1]});
		else res = min(res , dfs(u + 1 , i , j));
	}	
	return f[u][i][j] = res;
}

void Asuka()
{
	cin >> n >> k;
	for(int i = 1 ; i <= n ; i ++)
		cin >> a[i];
	for(int i = 1 ; i <= n ; i ++)
		cin >> b[i];
	sort(a + 1 , a + 1 + n , greater());
	sort(b + 1 , b + 1 + n , greater());
	for(int i = 0 ; i <= 2 * n ; i ++){
		for(int j = 0 ; j <= k ; j ++){
			for(int z = 0 ; z <= k ; z ++){
				f[i][j][z] = -1e18;
			}
		}
	}
	cout << dfs(0 , 0 , 0) << "\n";
	return;
}	

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	// cin >> t;
	while(t --){
		Asuka();
	}
	return 0;
}

Problem C. Catch You Catch Me(dfs)

  • 选择 1 的邻接点一定是最优的,dfs记录每个点的深度和每个点子结点的最大深度,答案就是 1 的所有邻接点的子结点最大深度
#include <bits/stdc++.h>

using namespace std;

#define int long long

void solve()
{
    int n; cin >> n;
    vector<vector<int>> g(n + 1);
    for (int i = 1; i < n; i ++ )
    {
        int u, v; cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    vector<int> deep(n + 1), ans(n + 1);
    function<void(int, int)> dfs = [&](int u, int fa)
    {
        for (auto v : g[u])
        {
            if (v == fa) continue;
            deep[v] = deep[u] + 1;
            ans[v] = deep[u] + 1;
            dfs(v, u);
            ans[u] = max(ans[u], ans[v]);
        }
    };
    dfs(1, -1);
    int res = 0;
    for (auto v : g[1]) res += ans[v];
    cout << res << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    int t = 1;
    // cin >> t;
    while (t -- )
    {
        solve();
    }
}

Problem D. Gambler’s Ruin(双指针)

  • 如果赔率为 x,某个人下注 c,队伍获胜概率 p,那么他期望获得: ( x − 1 ) × c × p − c × ( 1 − p ) (x-1)\times c\times p-c\times (1-p) (x1)×c×pc×(1p),该式大于等于 0,可以得到 p × x > 1 p\times x>1 p×x>1 时,这个人会下注
  • 因此我们按照 p 从大到小排序,可以发现一段前缀 p r e i pre_i prei 会下注第一个队伍获胜,一段后缀 s u f j suf_j sufj 会下注第二个队伍获胜,设第一个队伍赔率 x x x,第二个队伍赔率 y y y,有 1 p i ≤ y < 1 p i + 1 \frac{1}{p_i}\leq y<\frac{1}{p_{i+1}} pi1y<pi+11 1 p j ≤ x < 1 1 − p j − 1 \frac{1}{p_{j}}\leq x<\frac{1}{1-p_{j-1}} pj1x<1pj11
  • 目标是让 p r e i + s u f j − max ⁡ ( p r e i × x , s u f j × y ) pre_i+suf_j-\max(pre_i\times x, suf_j\times y) prei+sufjmax(prei×x,sufj×y) 最小,所以 x = 1 p i x=\frac{1}{p_i} x=pi1 y = 1 p j y=\frac{1}{p_j} y=pj1,此时答案即为 p r e i + s u f j − max ⁡ ( p r e i p i ,   s u f j 1 − p j ) pre_i+suf_j-\max(\frac{pre_i}{p_i},\ \frac{suf_j}{1-p_j}) prei+sufjmax(piprei, 1pjsufj)
  • i i i 固定时,一定会存在某个 p p p 使得 j ≤ p j\leq p jp 时, p r e i p i ≤   s u f j 1 − p j \frac{pre_i}{p_i}\leq\ \frac{suf_j}{1-p_j} piprei 1pjsufj,答案 p r e i + s u f j − s u f j 1 − p j pre_i+suf_j-\frac{suf_j}{1-p_j} prei+sufj1pjsufj,而 j > p j>p j>p 时, p r e i p i >   s u f j 1 − p j \frac{pre_i}{p_i}>\ \frac{suf_j}{1-p_j} piprei> 1pjsufj,答案 p r e i + s u f j − p r e i p i pre_i+suf_j-\frac{pre_i}{p_i} prei+sufjpiprei
  • 随着 i 增大,j 会变小,所以使用双指针
  • 注意不要用printf输出否则会t(?
#include <bits/stdc++.h>

using namespace std;

#define int long long
#define double long double

typedef pair<double, int> PDI;

void solve()
{
    int n; cin >> n;
    vector<PDI> a(n + 1), b(1);
    for (int i = 1; i <= n; i ++ ) cin >> a[i].first >> a[i].second;
    sort(a.begin() + 1, a.end(), greater<PDI>());
    for (int i = 1; i <= n; i ++ )
    {
        if (i == 1 || fabs(a[i].first - a[i - 1].first) > 1e-10) b.push_back(a[i]);
        else b[b.size() - 1].second += a[i].second;
    }
    int m = b.size() - 1;
    vector<int> pre(m + 1), suf(m + 2);
    for (int i = 1; i <= m; i ++ ) pre[i] = pre[i - 1] + b[i].second;
    for (int i = m; i >= 1; i -- ) suf[i] = suf[i + 1] + b[i].second;
    double ans = 0.0;
    for (int i = 1, j = m; i <= m; i ++ )
    {
        while (j > i && pre[i] / b[i].first > suf[j] / (1 - b[j].first)) j -- ;
        ans = max(ans, pre[i] + suf[j + 1] - 1.0 * pre[i] / b[i].first);
        if (i >= j) break;
        ans = max(ans, pre[i] + suf[j] - 1.0 * suf[j] / (1 - b[j].first));
    }
    cout << fixed << setprecision(10) << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    int t = 1;
    // cin >> t;
    while (t -- )
    {
        solve();
    }
}

Problem E. Hammer to Fall(根号分治)

在这里插入图片描述

#include <bits/stdc++.h>

using namespace std;

#define int long long

typedef pair<int, int> PII;
const int N = 1e5;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;

void solve()
{
    int n, m, q; cin >> n >> m >> q;
    vector<int> a(n + 1), ind(n + 1);
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    vector<vector<PII>> g(n + 1);
    for (int i = 1; i <= m; i ++ )
    {
        int u, v, w; cin >> u >> v >> w;
        g[u].push_back({v, w});
        g[v].push_back({u, w});
        ind[u] ++ ; ind[v] ++ ;
    }
    vector<int> b(q + 1);
    for (int i = 1; i <= q; i ++ ) cin >> b[i];

    // 记录所有二类点的邻接点中的二类点
    int X = 600;
    vector<vector<PII>> big(n + 1);
    vector<int> dp(n + 1);
    vector<multiset<PII>> mst(n + 1);
    for (int i = 1; i <= n; i ++ )
    {
        for (auto t : g[i])
        {
            int v = t.first, w = t.second;
            if (ind[v] <= X) continue;
            big[i].push_back({v, w});
            mst[v].insert({dp[i] + w, i});
        }
    }
    
    // 根号分治更新每个点答案
    auto work1 = [&](int v)
    {
        int cur = dp[v];
        dp[v] = INF;
        for (auto t : g[v])
        {
            int u = t.first, w = t.second;
            dp[v] = min(dp[v], dp[u] + w);
        }
        for (auto t : big[v])
        {
            int u = t.first, w = t.second;
            mst[u].erase({cur + w, v});
            mst[u].insert({dp[v] + w, v});
        }
    };
    auto work2 = [&](int v)
    {
        int cur = dp[v];
        dp[v] = (*mst[v].begin()).first;
        for (auto t : big[v])
        {
            int u = t.first, w = t.second;
            mst[u].erase({cur + w, v});
            mst[u].insert({dp[v] + w, v});
        }
    };
    for (int i = q; i >= 1; i -- )
    {
        int v = b[i];
        if (ind[v] <= X) work1(v);
        else work2(v);
    }
    int ans = 0;
    for (int i = 1; i <= n; i ++ )
    {
        ans = (ans + dp[i] * a[i] % mod) % mod;
    }
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    int t = 1;
    // cin >> t;
    while (t -- )
    {
        solve();
    }
}

Problem G. Let Them Eat Cake(模拟)

  • 暴力模拟
  • 不想写了偷队友的码用用
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
#include <iomanip>
#include<random>
#include<set>
using namespace std;
const int N = 1e5 + 10;
int n;
void Asuka()
{
	cin >> n;
	vector<int>a , b;
	for(int i = 0 ; i < n ; i ++){
		int x;cin >> x;
		a.emplace_back(x);
	}
	int res = 0;
	bool flag = false;
	while(1){
		int cnt = 0;
		if(!flag){
			if(a.size() == 1)break;
			for(int i = 0 ; i < a.size() ; i ++){
				if(i != (int)(a.size() - 1) && a[i] < a[i + 1]){
					continue;
				}else if(i != 0 && a[i] < a[i - 1]){
					continue;
				}else{
					b.emplace_back(a[i]);
				}
			}
			a.clear();
		}else{
			if(b.size() == 1)break;
			for(int i = 0 ; i < b.size() ; i ++){
				if(i != (int)(b.size() - 1) && b[i] < b[i + 1]){
					continue;
				}else if(i != 0 && b[i] < b[i - 1]){
					continue;
				}else{
					a.emplace_back(b[i]);
				}
			}
			b.clear();
		}
		res ++;
		flag ^= 1;
	}	
	cout << res << '\n';
	return;
}	

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	// cin >> t;
	while(t --){
		Asuka();
	}
	return 0;
}

Problem H. Life is Hard and Undecidable, but…(构造)

  • 想到了横着的线没想到对角线…被题目给的例子硬控了好久
#include <bits/stdc++.h>

using namespace std;

#define int long long

typedef pair<int, int> PII;

void solve()
{
    int k; cin >> k;
    vector<PII> ans;
    ans.push_back({150, 150});
    k -- ;
    int tmp = 150;
    for (int i = 1; i <= k; i ++ )
    {
        ans.push_back({tmp - i, tmp - i});
        ans.push_back({tmp + i, tmp + i});
    }
    cout << ans.size() << '\n';
    for (auto t : ans) cout << t.first << ' ' << t.second << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    int t = 1;
    // cin >> t;
    while (t -- )
    {
        solve();
    }
}

Problem J. Middle Race(二分)

在这里插入图片描述

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int INF = 0x3f3f3f3f3f3f3f3f;

void solve()
{
    int n, A, B, C; cin >> n >> A >> B >> C;
    if (A < B) swap(A, B);
    if (A < C) swap(A, C);
    if (B < C) swap(B, C);
    auto cal = [&](int a, int b)
    {
        return a * (A - C) + b * (B - C) + n * C;
    };

    int cnta = 0, cntb = 0, dif = INF;
    for (int a = 0; a <= n; a ++ )
    {
        int l = 0, r = n - a;
        while (l < r)
        {
            int mid = l + r >> 1;
            if (cal(a, mid) * 3 >= (A + B + C) * n) r = mid;
            else l = mid + 1;
        }
        if (r >= 1 && llabs(cal(a, r) * 3 - (A + B + C) * n) > llabs(cal(a, r - 1) * 3 - (A + B + C) * n)) r -- ;
        if (llabs(cal(a, r) * 3 - (A + B + C) * n) < dif)
        {
            dif = llabs(cal(a, r) * 3 - (A + B + C) * n);
            cnta = a, cntb = r;
        }
    }
    for (int i = 1; i <= cnta; i ++ )
    {
        cout << A << endl;
        int c; cin >> c >> c;
    }
    for (int i = 1; i <= cntb; i ++ )
    {
        cout << B << endl;
        int c; cin >> c >> c;
    }
    for (int i = 1; i <= n - cnta - cntb; i ++ )
    {
        cout << C << endl;
        int c; cin >> c >> c;
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    int t = 1;
    cin >> t;
    while (t -- )
    {
        solve();
    }
}

Problem M. Rock-Paper-Scissors Pyramid(思维)

  • 把遇到的字符放进栈中,每次先判断能否赢栈顶,可以就弹出栈顶继续判断,不能就直接入栈
  • 最后答案就是栈的第一个数(不是栈顶)
#include <bits/stdc++.h>

using namespace std;

#define int long long

typedef pair<char, char> PCC;

void solve()
{
    string s; cin >> s;
    stack<char> stk;
    map<PCC, bool> mp;
    mp[{'R', 'R'}] = mp[{'R', 'S'}] = 1;
    mp[{'S', 'S'}] = mp[{'S', 'P'}] = 1;
    mp[{'P', 'P'}] = mp[{'P', 'R'}] = 1;
    for (int i = 0; i < s.size(); i ++ )
    {
        if (i == 0) stk.push({s[i]});
        else
        {
            while (!stk.empty())
            {
                if (mp[{s[i], stk.top()}]) stk.pop();
                else break;
            }
            stk.push(s[i]);
        }
    }
    char ans;
    while (stk.size())
    {
        ans = stk.top();
        stk.pop();
    }
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    int t = 1;
    cin >> t;
    while (t -- )
    {
        solve();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Texcavator

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值