CodeChef Starters 6 Division 3 题解

本场链接: CodeChef Starters 6 Division 3 (Rated)

Cricket Ranking

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int r1,r2,w1,w2,c1,c2;scanf("%d%d%d%d%d%d",&r1,&w1,&c1,&r2,&w2,&c2);
        int cnt = 0;
        if(r1 > r2) ++cnt;
        if(w1 > w2) ++cnt;
        if(c1 > c2) ++cnt;
        if(cnt >= 2)    puts("A");
        else puts("B");
    }
    return 0;
}

Three Dices

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int x,y;scanf("%d%d",&x,&y);
        if(x + y >= 6)   puts("0");
        else    printf("%.18lf\n",(6 - x - y) / 6.0);
    }
    return 0;
}


Joker and Batman

把各个元素标记一下来自哪个集合,再处理最长段即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 1e5+7;
int from[N],a[N];

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n,m,l;scanf("%d%d%d",&n,&m,&l);
        forn(i,1,m)
        {
            int k;scanf("%d",&k);
            while(k--)
            {
                int x;scanf("%d",&x);
                from[x] = i;
            }
        }
        forn(i,1,l) scanf("%d",&a[i]);
        int res = 0;
        forn(i,1,l)
        {
            int j = i;
            while(j + 1 <= l && from[a[j + 1]] == from[a[i]])   ++j;
            ++res;
            i = j;
        }
        printf("%d\n",res);
    }
    return 0;
}


Even tuples

首先数值没有意义,直接将所有元素踢成1/0.

其次注意到要使一段结果是偶数只有两种情况:三个元素全为偶数,或者三个元素中有两个是奇数.考虑直接组合:前者情况相当于在区间中所有的偶数里选出三个;后者相当于先从奇数里选出两个,剩下一个偶数可以在区间里任选,方案数由乘法原理相乘合并.

由于选出的数只有1/2/3所以可以直接把表达式写出来.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 1e5+7;
int a[N],c[N][2];

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n,q;scanf("%d%d",&n,&q);
        forn(i,1,n) scanf("%d",&a[i]);
        forn(i,1,n)
        {
            c[i][0] = c[i - 1][0];c[i][1] = c[i - 1][1];
            ++c[i][a[i] % 2];
        }
        while(q--)
        {
            int l,r;scanf("%d%d",&l,&r);
            ll res = 0;
            int u0 = c[r][0] - c[l - 1][0],u1 = c[r][1] - c[l - 1][1];
            if(u0 >= 3) res = (res + 1ll * u0 * (u0 - 1) * (u0 - 2) / 6);
            if(u0 >= 1 && u1 >= 2)  res = (res + 1ll * u1 * (u1 - 1) / 2 * u0);
            printf("%lld\n",res);
        }
    }
    return 0;
}

Chefs Homework Dilemma

因为每天只有两种选择:要么做要么不做.考虑dp:

  • 状态:\(f[i]\)表示前\(i\)天的最少时间.但是有个问题,因为这样表示状态不能通过前面的已知状态得到这个人上一次做作业是什么时候:可以如此修改状态表示:\(f[i]\)表示前\(i\)天且强制\(i\)天时做作业的最少时间.
  • 入口:\(f[0] = 0\).
  • 转移:\(f[i] = \min\{f[i - k - 1],f[i - k]...f[i - 1]\} + H[i]\).
  • 出口:\(f[n + 1]\).因为\(f[n]\)这个状态没有包含:第\(n\)天的时候不做作业的情况,所以不妨做到第\(n+1\)天并强制\(H[n + 1] = 0\).

由于这个题卡的比较紧,数据范围是\(10^6\)且只有0.5s,所以直接套RMQ可能不太稳,一种稳定线性的做法是:使用单调队列维护前面一个长度为\(k+1\)的区间,保证队头是最小值.如此可以使得整个转移是线性的.

注意维护区间的长度是\(k+1\)而不是\(k\).

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)

const int N = 1e6+7;
int H[N],f[N],q[N];

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n,k;scanf("%d%d",&n,&k);
        forn(i,1,n) scanf("%d",&H[i]);H[n + 1] = 0;
        
        int hh = 0,tt = 0;q[0] = 0;f[0] = 0;
        forn(i,1,n + 1)
        {
            while(hh <= tt && i - q[hh] > k + 1)    ++hh;
            f[i] = f[q[hh]] + H[i];
            while(hh <= tt && f[q[tt]] >= f[i]) --tt;
            q[++tt] = i;
        }
        printf("%d\n",f[n + 1]);
    }
    return 0;
}

Palindromic number

(其实感觉想的难度还没上一个题目高)

题目其实是可以看做有两问的:是否可以变成一个回文串以及能做出来的回文串字典序最大的方案.

考虑(1)问:枚举每个字符对,找到一个\(t\)使得:从\(s[i]\)转移到\(t\)加上\(s[n - i + 1]\)转移到\(t\)的代价和最小.可以联想到题目的转换操作其实等价于在图上移动点,从起点走到终点对应把起点的字符转换为终点的字符的过程,边权之和就是代价.所以按题目所给的条件建边,求出任意两对元素之间的最短路即可等价于转换到\(t\)的代价.这个部分可以丢个floyd跑.

考虑(2)问:因为字典序是从前往后一个一个看的,所以一个经典的策略就是从前往后一个一个看能不能搞个最大的来.具体来说:从前往后遍历,倒着枚举目标字符看能不能做出来,如果可以搞就直接替换并把代价扣过去,但是如果在(1)的时候把s修改成了一个合法的回文串,在(2)直接做就会出错,但是直接做(2)这样的贪心又不好保证至少是一个回文串.

问题是,如果记s'为回文串的话((1)问得到的),那么从s[i] -> s'[i] -> target的路径不一定会比s[i] -> target更短,所以在做(2)问的时候,代价应该是:s->target的代价减去s->s'的代价.相当于是把(1)问倒回去再计算.这样做就可以保证最短路的正确性.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define int ll

const int N = 1e5+7,M = 12;
int dist[M][M];
char s[N],P[N];
int n,m,k;

inline int gcost(int p,int t,char* s)
{
    int x = s[p] - '0',y = s[n - p + 1] - '0';
    if(p == n - p + 1)  return dist[x][t];
    return dist[x][t] + dist[y][t];
}

signed main()
{
    int T;scanf("%lld",&T);
    while(T--)
    {
        forn(i,0,9) forn(j,0,9) dist[i][j] = 1e18;
        forn(i,0,9) dist[i][i] = 0;

        scanf("%lld%lld%lld",&n,&m,&k);
        scanf("%s",s + 1);forn(i,1,n)   P[i] = s[i];
        forn(i,1,m)
        {
            int x,y,k;scanf("%lld%lld%lld",&x,&y,&k);
            dist[x][y] = min(dist[x][y],k);
        }
        forn(k,0,9)    forn(i,0,9) forn(j,0,9)  dist[i][j] = min(dist[i][j],dist[i][k] + dist[k][j]);

        bool ok = 1;
        forn(i,1,(n + 1) / 2)
        {
            if(s[i] == s[n - i + 1])    continue;
            int mint = 0,mincost = gcost(i,0,s);
            forn(t,1,9) if(gcost(i,t,s) < mincost)   mincost = gcost(i,t,s),mint = t;
            if(k < mincost)
            {
                ok = 0;
                break;
            }
            s[i] = s[n - i + 1] = mint + '0';
            k -= mincost;
        }

        if(!ok) puts("-1");
        else
        {
            forn(i,1,(n + 1) / 2)
            {
                forr(t,s[i] - '0' + 1,9)
                {
                    int cost = gcost(i,t,P) - gcost(i,s[i] - '0',P);
                    if(k >= cost)
                    {
                        k -= cost;
                        s[i] = s[n - i + 1] = t + '0';
                        break;
                    }
                }
            }
            printf("%s\n",s + 1);
        }
    }
    return 0;
}
posted @ 2021-07-24 21:58  随处可见的阿宅  阅读(88)  评论(16编辑  收藏  举报