Codeforces Round 916 (Div. 3) (A~F附带题解和详细思路)

Codeforces Round 916 (Div. 3) (A~E2)

A. Problemsolving Log

签到题,对于给出的字符串,用数组记录每个字母出现的次数,然后遍历一边记录数组,如果对应的字母出现次数大于它的位次,则说明该字母对应的题目被解出来了,最后输出解题数量即可

void solve()
{
    int n;
    cin >> n;
    string s;
    cin >> s;
    int st[27] = {0};
    int count = 0;
    for(int i=0;i<s.size();i++)
    {
        st[s[i]-'A'+1]++;
    }
    for(int i=1;i<27;i++)
    {
        if(st[i]>=i) count++;
    }
    cout << count << endl;
}

B. Preparing for the Contest

简单排序,要有k个升序的数,则前面排1 ~ k即可,后面从n ~ k+1降序排列

void solve()
{
    int n,k;
    cin >> n >> k;
    int i;
    for(i=1;i<=k;i++)
    {
        cout << i << " ";
    }
    for(int j=n;j>=k+1;j--)
    {
        cout << j << " ";
    }
    cout << endl;
}

C. Quests

贪心+暴力题,首先预处理出a的前缀和maxa,并且维护一个maxb[i],表示b[i]前i个数中的最大值,然后从1~n遍历,每一次获得的经验应该是maxa[i] + (k-i)*maxb[i],然后取最大值即可,记得处理一下边界情况

  • if(i > k) break;
  • (k - i >= 0 ? k - i : 0)
#define int long long
const int N = 2e5 + 10;
int a[N], b[N];
int maxa[N], maxb[N];
void solve()
{
    int n, k;
    int mmax = 0;
    cin >> n >> k;
    maxa[0] = maxb[0] = 0;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        maxa[i] = maxa[i - 1] + a[i];
    }
    for (int i = 1; i <= n; i++)
    {
        cin >> b[i];
        maxb[i] = max(maxb[i - 1], b[i]);
    }
    for (int i = 1; i <= n; i++)
    {
        if(i > k) break;
        int tmp = maxa[i] + maxb[i] * (k - i >= 0 ? k - i : 0);
        mmax = max(mmax, tmp);
    }
    cout << mmax << endl;
}

D. Three Activities

无脑暴力打表,记得关闭io流同步哦,不然会TLE

PII a[N],b[N],c[N];

void solve()
{
    int n;
    cin >> n;
    for(int i=0;i<n;i++)
    {
        cin >> a[i].first;
        a[i].second = i;
    }
    for(int i=0;i<n;i++)
    {
        cin >> b[i].first;
        b[i].second = i;
    }
    for(int i=0;i<n;i++)
    {
        cin >> c[i].first;
        c[i].second = i;
    }
    sort(a,a+n);
    sort(b,b+n);
    sort(c,c+n);
    int max1 = a[n-1].first; //a b c
    for(int i=n-1;i>=0;i--)
    {
        if(b[i].second!=a[n-1].second)
        {
            max1 += b[i].first;
            for(int j = n-1;j>=0;j--)
            {
                if(c[j].second!=a[n-1].second && c[j].second!= b[i].second)
                {
                    max1 += c[j].first;
                    break;
                }
            }
            break;
        }
    }
    int max2 = b[n-1].first; // b a c
    for(int i=n-1;i>=0;i--)
    {
        if(a[i].second!=b[n-1].second)
        {
            max2 += a[i].first;
            for(int j = n-1;j>=0;j--)
            {
                if(c[j].second!=b[n-1].second && c[j].second!= a[i].second)
                {
                    max2 += c[j].first;
                    break;
                }
            }
            break;
        }
    }
    int max3 = c[n-1].first; //c a b
    for(int i=n-1;i>=0;i--)
    {
        if(a[i].second!=c[n-1].second)
        {
            max3 += a[i].first;
            for(int j = n-1;j>=0;j--)
            {
                if(b[j].second!=c[n-1].second && b[j].second!= a[i].second)
                {
                    max3 += b[j].first;
                    break;
                }
            }
            break;
        }
    }
    int max4 = a[n-1].first; //a c b
    for(int i=n-1;i>=0;i--)
    {
        if(c[i].second!=a[n-1].second)
        {
            max4 += c[i].first;
            for(int j = n-1;j>=0;j--)
            {
                if(b[j].second!=a[n-1].second && b[j].second!= c[i].second)
                {
                    max4 += b[j].first;
                    break;
                }
            }
            break;
        }
    }
    int max5 = b[n-1].first; //b c a
    for(int i=n-1;i>=0;i--)
    {
        if(c[i].second!=b[n-1].second)
        {
            max5 += c[i].first;
            for(int j = n-1;j>=0;j--)
            {
                if(a[j].second!=b[n-1].second && a[j].second!= c[i].second)
                {
                    max5 += a[j].first;
                    break;
                }
            }
            break;
        }
    }
    int max6 = c[n-1].first; //c b a
    for(int i=n-1;i>=0;i--)
    {
        if(b[i].second!=c[n-1].second)
        {
            max6 += b[i].first;
            for(int j = n-1;j>=0;j--)
            {
                if(a[j].second!=c[n-1].second && a[j].second!= b[i].second)
                {
                    max6 += a[j].first;
                    break;
                }
            }
            break;
        }
    }
    cout << max({max1,max2,max3,max4,max5,max6}) << endl;
}

E1&&E2 Game with Marbles

只要先按照a+b排序,然后模拟过程即可

int a[N],b[N];
PII c[N];
void solve()
{
    int n,sum = 0;
    cin >> n;
    for(int i=0;i<n;i++)
    {
        cin >> a[i];
    }
    for(int i=0;i<n;i++)
    {
        cin >> b[i];
    }
    for(int i=0;i<n;i++)
    {
        c[i].first = a[i] + b[i];
        c[i].second = i;
    }
    sort(c,c+n,[&](PII _1,PII _2){return _1.first > _2.first;});
    for(int i=0;i<n;i++)
    {
        if(i&1)
        {
            a[c[i].second] = 0;
            b[c[i].second] -= 1;
        }
        else
        {
            a[c[i].second] -= 1;
            b[c[i].second] = 0;
        }
    }
    for(int i=0;i<n;i++)
    {
        if(a[i]) sum+=a[i];
        else sum-=b[i];
    }
    cout << sum << endl; 
}

F. Programming Competition

这是一个树型结构+dfs的题目,其次,codeforces better的翻译有问题,题意的原意应该是,所有的节点都不能和自己同一颗树上的节点匹配,也就是他不能匹配他的所有上级,同时也不能匹配自己的所有下级

其次,我们在这里用邻接表来存树,这样方便我们dfs,然后,实际上,我们通过分治的思想可以发现,对于一个任意子树,他能够提供的匹配其实存在两种情况

以下约定子树大小指的是子树的子节点数量(包括本身)

  • 最大子树小于等于当前子树节点总和的1/2 那么在这种情况下,所有的子树均可以相互匹配(关于匹配方法:只需要任取两个最大的出来匹配,然后重复,最后一定可以全部匹配或者仅剩下一个节点未匹配(也就是子树总和为奇数的情况)),而这种情况下,我们可以直接确定出当前根节点子树能够提供的最大匹配量,无需再向下递归他的子树了,可以做出剪枝
  • 最大子树大于当前子树节点总和的1/2 那么在这种情况下,所有的除最大子树外的所有子树均可以跟最大子树匹配,因此能够提供的匹配量就是除了最大子树外的所有子树节点的总和,但是,因为最大子树上任然存在没有匹配的节点,也就是说当前子树的最大匹配量任然没有确定,我们需要向下递归进入最大子树

因此,事实上,我们递归的永远都是大于1/2的子树,因为小于1/2的子树,其最大匹配量已经确定,无需再递归,但是对于我们递归的这些子树,他们的部分节点可能在上一层的时候就匹配出去了,因此我们在递归函数里面应该再引入一个变量c来记录上一层匹配出去的数量,此时,我们本层递归能够匹配的量就是当前子树节点总和-c,但是!!!实际上,根据贪心的思想,我们发现,我们递归每一层子树的时候,根节点都是不参与匹配的,但是根节点又确实属于子树的一个节点,因此我们可以先将根节点匹配出去,然后剩下的子树匹配出去的节点数就是c-1了

至此,我们整个题目的思路就是以上所述,具体的实现请参照代码

const int N = 2e5 + 10;
int e[N], ne[N], h[N], idx, son[N];
//son(i)记录的是以节点i为根节点的子树的节点个数和
int ans = 0;

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int calcson(int u) //预处理计算每一个子树的节点个数
{
    son[u] = 1;
    for (int i = h[u]; ~i; i = ne[i])
    {
        son[u] += calcson(e[i]);
    }
    return son[u];
}

void dfs(int u, int c)
{
    if (c > 0) //将c中的一个匹配到当前子树的根节点上
        c--;
    int maxsontree = e[h[u]]; // 找当前根节点的最大子树
    int sonsum = 0; //统计子树和
    for (int i = h[u]; ~i; i = ne[i]) //枚举每颗子树
    {
        sonsum += son[e[i]];
        if (son[maxsontree] < son[e[i]])
        {
            maxsontree = e[i];
        }
    }
    if (son[maxsontree] - c <= sonsum - son[maxsontree]) // 如果最大的子树小于当前总节点数的1/2
    {
        ans += (sonsum - c) >> 1; //答案就是子树和-c除以二向下取整
    }
    else //如果最大的子树大于当前总节点数的1/2
    {
        ans += sonsum - son[maxsontree]; 
        dfs(maxsontree, c + sonsum - son[maxsontree]); //递归最大子树
    }
}

void solve()
{
    ans = 0;
    memset(h, -1, sizeof h);
    int n;
    cin >> n;
    for (int i = 2; i <= n; i++) //建树
    {
        int t;
        cin >> t;
        add(t, i);
    }
    calcson(1); //计算每颗子树的节点数
    dfs(1, 0); 
    cout << ans << endl;
}
posted @ 2023-12-20 01:27  橙之夏  阅读(444)  评论(0)    收藏  举报