Educational Codeforces Round 169 (Rated for Div. 2)(A-D)

题目链接

目录

A

B

C

D


A

根据题意,需要新增一个点,使得该点到集合中每一个点的距离最近。
分情况讨论:

  1. 集合元素大于2个:一定不存在这样的点
  2. 集合元素等于2个(不能小于,题目范围给定最少2个):当这两个元素之差大于1时,存在一点满足要求。该点的位置可以位于两点之间任意位置。
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<cmath>
using namespace std;
using ll = long long;
void slove()
{
    int n; cin >> n;
    vector<int> a(n, 0);
    for(int i = 0; i < n; i++) cin >> a[i];
    if(n > 2) { cout << "NO" << endl; return; }
    if(abs(a[1] - a[0]) >= 2) { cout << "YES" << endl; return; }
    cout << "NO" << endl;
}  

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t; cin >> t;
    while(t--) slove();
    return 0;
}

B

对于两个人位于的区间重合情况分类讨论:

  1. 重合区间左右端点都在整个区间内部,应该将这个重合区间的所有门,包括边上两个门也关闭
  2. 重合区间左右端点有一边在整个区间边上,那么这个边上的门不需要关闭,比情况1少一个门
  3. 重合区间左右两个端点都在整个区间边上,等价于重合区间就是整个区间。那么两边的门都不需要关,比情况1少两个门。
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<cmath>
using namespace std;
using ll = long long;
void slove()
{
    int l1, r1, l2, r2; cin >> l1 >> r1 >> l2 >> r2;
    if(r1 < l2 || l1 > r2) { cout << 1 << endl; return; }
    int l = max(l1, l2), r = min(r1, r2);
    int x = min(l1, l2), y = max(r1, r2);
    if(l > x && r < y) cout << r - l + 2 << endl;
    else if(l > x || r < y) cout << r - l + 1 << endl;
    else cout << r - l << endl;
}  

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t; cin >> t;
    while(t--) slove();
    return 0;
}

C

保证分数最大,a、b每次取最大值。对于b来说,可以提前改变数组,累加增加k。b每次取值在a之后,那么b只能依次取次大值,所以b需要改变数组的依次次大值,并且不能超过依次最大值,否则a就会取该值,会导致分数变大。

#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<cmath>
using namespace std;
using ll = long long;
void slove()
{
    int n, k; cin >> n >> k;
    vector<int> a(n + 1, 0);
    for(int i = 1; i <= n; i++) cin >> a[i];
    sort(a.begin() + 1, a.end(), greater<int>());
    int res = 0;
    for(int i = 1; i <= n; i++)
    {
        if(i & 1) res += a[i];
        else
        {
            int add = min(k, a[i - 1] - a[i]);
            a[i] += add;
            k -= add;
            res -= a[i];
        }
    }
    cout << res << endl;
}  

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t; cin >> t;
    while(t--) slove();
    return 0;
}

D

城市 x,和城市 y,之间的最短路径为 | x - y |;一共四种颜色,有相同即可达;那么最多只会通过一个城市进行中转,这样才能保证路径最短。
证明:
为了不失一般性,设四种颜色分别为(a,b,c,d);城市 x 的颜色为(a,b),城市 y 的颜色为(c,d)(当两个城市不能直达)。设两个中转城市 A、B,为了保证能够中转,需要 A 和 B 中的颜色应该包括 x 和 y 中的至少一个颜色,并且 A 和 B 需可直达。设路径为 x -- > A -- > B -- > y,那么 A 中至少有一个颜色和 x 相同,B 中至少有一个和 y 相同。不妨设有一个颜色相同。
A(a,b)or (a,c);B(d,b) or (d, c)。观察发现不需要两个中转城市,通过一个中转城市即可达到目标城市。

设中转城市为 z。x -- > z -- > y。对于 z 的存在与否,位置,有下面四种情况:(设 x < y)

  1. 不需要中转,res = y - x
  2. z 位于 x 和 y 之间, res = y - x
  3. z 位于 x 之前,res = y - x + 2 * (x - z)
  4. z  位于 y 之后,res = y - x + 2 * (z - y)

情况1和情况2的路径是最短的。

当 x 和 y 不可直达时,需要借助中转城市 z,z 的颜色情况可以通过 x 和 y 的颜色情况暴力枚举得出。
首先预处理,将每一种颜色的城市下标保存 map<int, vector<int>> 中,得到 z 的颜色情况之后;根据情况2、3、4,计算每种情况的路径,取最短。
对于情况3:越靠近 x ,路径越短
对于情况4:越靠近 y ,路径越短

如何判断 z 的位置?
对于城市 z 的颜色情况,可以得到该种颜色情况的所有城市下标,并且是升序的(在预处理阶段,是从左往右遍历)。存储在 vector 中,这些城市都是 z 的可能。
那么可以在 vector 中二分查找第一个大于等于 x 的位置(使用lower_bound)。当这个位置存在,且小于等于 y ,对应情况2。
查找第一个大于 y 的位置(upper_bound),对于情况4
在情况2的基础上,假如 z 位置前还有小于 x 的元素,那么这个元素就是最靠近 x 的元素(左边靠近)

#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<map>
using namespace std;
using ll = long long;
const int N = 2e5 + 10, INF = 1e9;
string s[N];


void slove()
{
    map<string, vector<int>> hs;
    int n, q; cin >> n >> q;
    for(int i = 1; i <= n; i++)
    {
        cin >> s[i];
        hs[s[i]].push_back(i);  // 保证 vector 都为升序排列
    }
    
    while(q--)
    {
        int x, y; cin >> x >> y;
        int res = INF;
        if(x > y) swap(x, y);  // 规定x < y
        
        for(int i = 0; i < s[x].size(); i++)
        {
            bool f = false;
            for(int j = 0; j < s[y].size(); j++)
            {
                if(s[x][i] == s[y][j])  // 当 x 和 y 可直达,y - x 一定为最短;可提前结束
                {
                    res = min(res, y - x);
                    f = true;
                    break;
                }
            
                string str;
                str += s[x][i];
                str += s[y][j];
                if(str[0] > str[1]) swap(str[0], str[1]);  // 根据题目给出的颜色组合情况,两个颜色是升序排列
                
                if(!hs.count(str)) continue; // 假如城市 z 的该种颜色组合不存在,可丢弃
                
                
                vector<int>& nums = hs[str];
                auto mid = lower_bound(nums.begin(), nums.end(), x);  // 当 z 位于 x 和 y 的中间
                if(mid != nums.end() && (*mid) <= y) res = min(res, y - x);
                
                auto pre = mid;
                auto suf = upper_bound(nums.begin(), nums.end(), y);
                if(pre != nums.begin()) { pre -- ; res = min(res, y - x + 2 * (x - (*pre))); }  // x 的左边第一个
                if(suf != nums.end()) res = min(res, y - x + 2 * ((*suf) - y)); // y 的右边第一个
            }
            
            if(f) break;
        }
        if(res == INF) res = -1;
        cout << res << endl;
    }
    
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t; cin >> t;
    while (t--) slove();
    return 0;
}



 

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值