Codeforces Round 888 (Div. 3)

Dashboard - Codeforces Round 888 (Div. 3) - Codeforces

A. Escalator Conversations

题意:

有n人,m级台阶,每级台阶的高度为k,主人公的身高为H,第二行人n人的高度hi。问主人公最多能与几个人站在同等高度聊天(主人公不能与其他人站在同一台阶)。(大意是这样的...

题解:

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
void solve()
{
    int n, m, k, h, ans = 0;
    scanf("%d%d%d%d", &n, &m, &k, &h);
    for (int i = 1, x; i <= n; ++i)
    {
        scanf("%d", &x);
        x = abs(x - h);
        if (x != 0 && x % k == 0 && x / k < m)++ans;
    }
    printf("%d\n", ans);
}
int main()
{
    int T = 1;
    scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}

B. Parity Sort

题意:

给出一个序列a,你只能交换数列中的奇数或者偶数,问你最终能否使整个序列升序

题解:

可以对序列排序然后遍历一遍看看每个位置上的数字与原数组的奇偶性是否一致,若一致则为YES。我写了一个若至一点的做法,记录每个位置的奇偶然后将奇数与偶数拿出来分别排序再放回去看是否升序...这里还是给一个前一种做法的码

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
int a[N], f[N];
void solve()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
    {
        scanf("%d", &a[i]);
        f[i] = a[i] % 2;
    }
    sort(a + 1, a + 1 + n);
    for (int i = 1; i <= n; ++i)
    {
        if (a[i] % 2 != f[i])
        {
            printf("NO\n");
            return;
        }
    }
    printf("YES\n");
}
int main()
{
    int T = 1;
    scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}

C. Tiles Comeback

题意:

题目给出n个格子和一个k,第二行给出n个格子的颜色ci,当Vlad跳过这些格子时经过的格子将会形成一个颜色序列:c1,c2,c2...cp。

Vlad希望获得这样一个序列:1、从格子1开始到格子n结束,2、经过的格子总数能被k整除,3、每连续k个格子的颜色是相同的(如C1=C2=...=Ck,Ck+1=Ck+1=...=C2*k)。问是否存在这样的一个序列。

题解:

贪心即可。若c1==cn,则只需要找一个由k个c1组成的序列,若c1!=cn,则只需要前k个是c1,后k个是cn即可。

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
int a[N];
void solve()
{
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    int l, s;//先找到第一个c1个数等于k的下标↓
    for (l = 1, s = 0; l <= n && s < k; ++l)
        if (a[l] == a[1])++s;
    if (s == k && a[n] == a[1])//存在k个数等于c1且c1=cn
    {
        printf("YES\n");
        return;
    }
    s = 0;
    for (int i = l; i <= n; ++i)
        if (a[i] == a[n])++s;//技术从l开始有几个cn
    if (s >= k)
        printf("YES\n");
    else
        printf("NO\n");
}
int main()
{
    int T = 1;
    scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}

D. Prefix Permutation Sums

题意:

题目给出一个n,第二行有n-1个数表示缺了一个数的前缀和数组(是前缀和数组里缺了一个不是原数组),问是否能够还原出一个长度为n的排列(排列指 数组元素为1到n且每个数仅出现一次)。

题解:

先将前缀和数组还原出一个原数组a。记录其中多余的数(重复的数或者值大于n的数)与缺少的数,若多余的数刚好是俩缺少的数之和,则说明是多余数的地方刚好缺了一个前缀和数。特别的当缺少的数刚好是最后一个元素时还原出的a数组中不会有多余的数,会有刚好一个缺少的数。

嗨害嗨,又爆int被hack喽(各位别学我)

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
LL a[N];
int cnt[N];
void solve()
{
    vector<LL>v1, v2;
    int n;
    scanf("%d", &n);
    memset(cnt, 0, sizeof(int) * (n + 1));
    for (int i = 1; i < n; ++i)
    {
        scanf("%lld", &a[i]);
        if (a[i] - a[i - 1] <= n)cnt[a[i] - a[i - 1]]++;//计数,用于找出多余的数
        else v1.push_back(a[i] - a[i - 1]);//出现大于n的数的情况说明该数是多余的
    }
    for (int i = 1; i <= n; ++i)
    {
        if (cnt[i] == 2)v1.push_back(i);//出现俩次,多余
        if (cnt[i] == 0)v2.push_back(i);//这玩意为啥没了
    }
    //缺少元素的位置在数组开头或中间,还原出的临时原数组中多余的数刚好能拆成缺少的俩数||缺了最后一个
    if ((v1.size() == 1 && v2.size() == 2 && v1[0] == v2[0] + v2[1]) || (v1.size() == 0 && v2.size() == 1))
        printf("YES\n");
    else
        printf("NO\n");
}

int main()
{
    int T = 1;
    scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}

E. Nastya and Potions

题意:

给出n种药水,其中有k种可以免费获取,第二行给出n种药水的售价,第三行给出k种能白嫖的药水的编号,接下来n行每行开头一个mi后面跟mi个编号表示第i种药水能由以下mi种药水合成(若mi为0则该药水只能买不能做)。求出获取每种药水的最小代价(付出金钱的代价)

题解:

可以用类似于dijkstra那样更新每种药水的最小获取价值即可。看tag应该还有很多别的做法。

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, int> PII;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
LL c[N], ans[N], s[N], book[N];
vector<int>v[N];//记录每种药水能够制取的药水种类
priority_queue<PII, vector<PII>, greater<PII> >q;
void solve()
{
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; ++i)
    {
        v[i].clear();//初始化
        s[i] = book[i] = 0;
    }
    for (int i = 1; i <= n; ++i)
        scanf("%lld", &c[i]);
    for (int i = 1, x; i <= k; ++i)
    {
        scanf("%d", &x);
        c[x] = 0;//这些能够零元购
    }
    for (int i = 1, m; i <= n; ++i)
    {
        scanf("%d", &m);
        for (int j = 1, x; j <= m; ++j)
        {
            scanf("%d", &x);
            v[x].push_back(i);//x能够制取y
            s[i] += c[x];
        }
        q.push({ c[i],i });//存入购买每种药水的代价
        if (m)q.push({ s[i],i });//与制取每种药水的代价
    }
    while (q.size())
    {
        int x = q.top().first, y = q.top().second;
        q.pop();
        if (book[y])continue;
        book[y] = 1, ans[y] = x;//确定药水y的最小代价
        for (auto v : v[y])//用y的代价去更新y能制取出的药水的代价
        {
            s[v] -= c[y] - x;//制取药水v的代价减少了这么多
            q.push({ s[v],v });
        }
    }
    for (int i = 1; i <= n; ++i)
        printf("%lld ", ans[i]);
    printf("\n");
}

int main()
{
    int T = 1;
    scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}

F. Lisa and the Martians

题意:

给出一个n和k,第二行给出n和元素ai(ai<2^k),定义一种运算(ai⊕x)&(aj⊕x) (x<2^k),求使(ai⊕x)&(aj⊕x)最大的一组i,j,x(有多组输出任意一组即可)

题解:

位运算构造一个最大的数的思路基本都是贪心,找一对ai,aj基本一眼要写tire。

这里不考虑亦或的情况,仅考虑一位时x&y当两者都是1的情况是1;一者是0另一个是1的情况是0;都是0的情况是0,。当考虑亦或时,两者都是1则都亦或0,答案还是1;都是0的情况就都亦或1,答案就会变成1;01的情况无能为力还是0。

当我们确定aj时,只要先把所有的ai(i<j)都放入tire树,然后在从最高位开始贪心找ai的时候优先找与aj相同的即可,相同的位置即为1,题目需要求的x可以直接构造出来(如2^k - 1 - (ai&aj))。

然而这题除了tire树还有一种神奇的做法:sort之后ai与aj就在相邻元素之间找就行。ABC308G就是需要用到这种思路。可以证明:对于任何x < y < z,min(x ⊕ y, y ⊕ z) < x ⊕ z,因此在一个集合中最小的亦或值必然是两个相邻的数的亦或值(这题运算的最大值其实就是2^k-1-(ai^aj),求最大值相当于求最小的ai^aj)。证明咱不会证,个人的大致思路就是找从最高位开始找尽量相同的数时这两个数一定是相邻的数中的一对,这个结论挺好记也挺好用的。

#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
PII a[N];
int get_num(int x,int k)
{
    return (1 << k) - 1 - (a[x].first ^ a[x + 1].first);
}
void solve()
{
    int n, k, ans = 1;
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; ++i)
    {
        scanf("%d", &a[i].first);
        a[i].second = i;
    }
    sort(a + 1, a + 1 + n);
    for (int i = 2; i < n; ++i)
    {
        if (get_num(i, k) > get_num(ans, k))
            ans = i;
    }
    int i = a[ans].second, j = a[ans + 1].second, x = (1 << k) - 1 - (a[ans].first & a[ans + 1].first);
    printf("%d %d %d\n", i, j, x);
}

int main()
{
    int T = 1;
    scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}

G. Vlad and the Mountains

题意:

有n座山,每座山的高度为hi,有m座桥,每座桥ui,vi连接了山ui与山vi的山顶,当从山头i走向山走j时会消耗体力hj-hi(当hj-hi小于0时回复hi-hj点体力),当体力值小于0时你会当场死亡。有q组询问,每组询问问你初始有e点体力,能否从山头a走到山头b。

题解:

离线+并查集。对于每组询问相当于咱可以经过所有高度等于或者低于ha+e的山头。当我们处理一组a,b,e时,可以把所有连接山头高度大于ha+e的桥删除(因为这些桥无论如何都是走不了的),用剩下的桥对山头进行一个并查集,若a与b在同一个集合中则有e点初始体力的情况下a能够到b。

当我们对询问进行离线处理时可以将所有询问排序,优先处理ha+e小的询问,然后将所有连接山头高度低于ha+e的桥加入集合对山头进行并查集,这样我们会发现我们只需要在处理询问的时候不断加入桥然后进行并查集就能够依次处理所有询问。

(离线是好思路,用的还是没怎么熟悉赛时没想出来...)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N=2e5+10,INF=0x3f3f3f3f;
int ans[N],p[N],h[N],u[N],v[N],x[N],y[N],eg[N];
int find(int x)
{
    if(p[x]!=x)
        p[x]=find(p[x]);
    return p[x];
}
priority_queue<PII,vector<PII>,greater<PII> >e,q;
//e存边,按max(hu,hv)排序的小顶堆,q存询问,按hx+eg排序的小顶堆
//(懒得写cmp用的优先队列...)
void solve()
{
    int n,m,qe;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        p[i]=i;
        scanf("%d",&h[i]);
    }
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&u[i],&v[i]);
        e.push({max(h[u[i]],h[v[i]]),i});
    }
    scanf("%d",&qe);
    for(int i=1;i<=qe;++i)
    {
        scanf("%d%d%d",&x[i],&y[i],&eg[i]);
        q.push({h[x[i]]+eg[i],i});
    }
    while(q.size())
    {    //优先处理hx+eg小的询问
        int k=q.top().first,t=q.top().second;
        q.pop();
        while(e.size()&&e.top().first<=k)//将max(hu,hv)<=hx+eg的边对山并查集
        {
            int i=e.top().second;
            e.pop();
            p[find(u[i])]=find(v[i]);
        }
        if(find(x[t])==find(y[t]))
            ans[t]=1;
        else
            ans[t]=0;
    }
    while(e.size())e.pop();
    for(int i=1;i<=qe;++i)
    {
        if(ans[i])printf("YES\n");
        else printf("NO\n");
    }
    printf("\n");
}
int main()
{
    int T=1;
    scanf("%d",&T);
    while(T--)
    {
        solve();
    }
    return 0;
}

推荐一位大佬:Hytidel - 知乎 (zhihu.com)

G是看这位大佬的题解补的,太牛了

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值