Codeforces Global Round 28 VP 记录

Codeforces Global Round 28 VP 记录

Dashboard - Codeforces Global Round 28 - Codeforces

之前做过 G,赛时从 A 做到了 G,赛后做了 H,看题解会了 I1,I2 还不会。

CF2048A Kevin and Combination Lock

判断是否是 \(33\) 倍数即可。

Submission #347480891 - Codeforces

CF2048B Kevin and Permutation

显然每个数至多造成 \(k\) 次贡献,所以在 \(k,2k,\ldots\) 位置上放 \(1,2\ldots\),其余任意。

Submission #347480977 - Codeforces

CF2048C Kevin and Binary Strings

一定会选 \([1,n]\),假设 \([1,n]\) 第一个 \(1\) 后面的 \(0\) 是从低到高第 \(x\) 位,那么第二个串除去前导 \(0\) 一定是第 \(x\) 位为 \(1\),即长度一定为 \(x+1\),枚举即可。

Submission #347481612 - Codeforces

CF2048D Kevin and Competition Memories

\(d_i\) 表示一场比赛出现第 \(i\) 题时 Kevin 前面至少有多少人,即当 \(b_i\le a_1\)\(d_i=0\),否则为 \(d_i\) 为有多少 \(a_j\ge d_i\)。一场比赛的贡献是 \(d_i\) 最大值加一。所以 \(k\) 的答案就是将 \(d_i\) 排序后,下标是 \(k\) 的倍数的 \(d_i\) 之和,复杂度为调和级数 \(\mathcal{O}(n\log n)\)

Submission #347481931 - Codeforces

CF2048E Kevin and Bipartite Graph

一个颜色最多染 \(2n+m-1\) 条边,所以要求 \(2nm\le n(2n+m-1)\),即 \(m\le 2n-1\),否则一定无解。我们不妨求出 \(m=2n-1\) 的答案,然后取前 \(m\) 个点一定合法。考虑左边 \(2n\) 个点右边 \(2n-1\) 个点的答案,对于每个颜色 \(i\),将左边点 \(x+2i-2\)\(x+2i-1\) 和右边点 \(x\) 连边(左边编号对 \(n\) 取模) 即可。

Submission #347482473 - Codeforces

CF2048F Kevin and Math Class

建出笛卡尔树,每次对一个子树操作一定最优。因为一直对整体操作可得答案不超过 \(\log V\),于是可以考虑将 dp 换维,即设 \(f_{u,i}\) 表示 \(u\) 子树内,要求 \(i\) 次操作完,最少需要整体除多少。当 \(f_{u,i}\) 转移时如果 \(u\) 子树整体除了至少一次,可以由 \(f_{u,i}\larr \lceil\frac{f_{u,i-1}}{b_u}\rceil\) 转移得到。否则枚举左儿子子树用了 \(x\) 次操作,转移为 \(f_{u,i}\larr \max\{a_u,f_{ls,x},f_{rs,i-x}\}\),总复杂度为 \(\mathcal{O}(n\log^2 V)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int N = 2e5+5;
int ls[N],rs[N],n,st[N],top;
ll f[N][65],a[N],b[N];
void dfs(int u)
{
    if(ls[u])dfs(ls[u]);
    if(rs[u])dfs(rs[u]);
    for(int i = 0;i <= 60;i++)f[u][i] = 1e18;
    for(int i = 0;i <= 60;i++)for(int j = 0;i+j <= 60;j++)
        f[u][i+j] = min(f[u][i+j],max({f[ls[u]][i],f[rs[u]][j],a[u]}));
    for(int i = 1;i <= 60;i++)f[u][i] = min(f[u][i],(f[u][i-1]-1)/b[u]+1);
}
char buf[1<<21],*p1,*p2;
inline ll rd()
{
    char c;int f = 1;
    while(!isdigit(c = getchar()))if(c=='-')f = -1;
    ll x = c-'0';
    while(isdigit(c = getchar()))x = x*10+(c^48);
    return x*f;
}
int main()
{
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    for(int t = rd();t--;)
    {
        n = rd();top = 0;
        for(int i = 1;i <= n;i++)a[i] = rd(),ls[i] = rs[i] = 0;
        for(int i = 1;i <= n;i++)
        {
            b[i] = rd();
            while(top&&b[i] <= b[st[top]])top--;
            if(!top)ls[i] = i>1?st[1]:0;
            else ls[i] = rs[st[top]],rs[st[top]] = i;
            st[++top] = i;
        }
        dfs(st[1]);int ans = 0;
        while(f[st[1]][ans] > 1)ans++;
        printf("%d\n",ans);
    }
    return 0;
}

CF2048G Kevin and Matrices

用总方案数减去不合法方案数,不合法要求所有行 \(\max\) 都大于列 \(\min\)。假设某一行某一列不合法,假设交为 \((i,j)\),那么一定是第 \(i\)\(\max\)\((i,j)\),第 \(j\)\(\min\)\((i,j)\)。于是我们可以考虑容斥,如果钦定了两个格子 \((x_1,y_1),(x_2,y_2)\),那么可以得出 \((x_1,y_1)\ge (x_1,y_2)\ge (x_2,y_2)\ge (x_2,y_1)\ge (x_1,y_1)\),所以所有不合法的格子一定构成了一个矩形,枚举这个矩形是 \(i\)\(j\) 列的,最终答案为:

\[\begin{aligned} &\sum_{i=1}^n\sum_{j=1}^m\sum_{k=1}^v (-1)^{i+j}{n\choose i}{m\choose j}k^{i(m-j)}(v-k+1)^{j(n-i)}v^{(n-i)(m-j)} \\ &= \sum_{i=1}^n\sum_{k=1}^v (-1)^i{n\choose i}k^{im}v^{(n-i)m}\sum_{j=1}^m {m\choose i}(-k^{-i}(v-k+1)^{n-i}v^{i-n})^j \\ &= \sum_{i=1}^n\sum_{k=1}^v (-1)^i{n\choose i}k^{im}v^{(n-i)m}((1-k^{-i}(v-k+1)^{n-i}v^{i-n})^m-1) \\ \end{aligned} \]

复杂度为 \(\mathcal{O}(nv\log m)\)

Submission #347484315 - Codeforces

CF2048H Kevin and Strange Operation

给定一个 01 串 \(s\),定义一次操作为:

  • 选择一个 \(1\le i\le |s|\)\(i\),对每个 \(1\le j < i\),同时执行 \(s_i\larr\max(s_i,s_{i+1})\),然后删去 \(s_i\)

求在任意次操作后能得到多少个本质不同的串。答案对 \(998244353\) 取模。

\(|s|\le 10^6\)

考虑删去一些位置后剩余位置会变成什么,对于某个剩下的 \(s_i\) 如果是 \(1\) 就不会变,否则是 \(0\) 的话找到下一个 \(1\) 距到自己之间有 \(x\)\(0\),那么这个 \(0\) 会变成 \(1\) 当且仅当 \(>i\) 的数操作了 \(x\) 次。所以可以看出操作顺序不重要,考虑求 \(s\) 能不能变为某个 \(t\),即找出 \(s\) 的一个子序列能匹配 \(t\)

如果 \(s,t\) 结尾都是 \(0\)\(1\),那么将它们的结尾都删去一定最优,因为如果 \(t\) 最后一位匹配了 \(s\) 前面的一个数并删去了 \(s\) 后面的数,和 \(t\) 匹配 \(s\) 最后一个数并删一些数是等价的。而如果 \(s\) 最后是 \(1\)\(t\) 最后是 \(0\) 一定无解,如果 \(s\) 最后是 \(1\)\(t\) 最后是 \(0\),就是不断操作 \(s\) 最后一个数直到 \(s\) 的末尾是 \(1\),此时再匹配。

于是我们可以看成是一个子序列自动机,即从后往前跳每次遇到 \(0\)\(1\) 时就往前跳到最近的位置,我们可以设 \(f_{i,j}\) 表示到第 \(i\) 个数,后面删了 \(j\) 个数的路径数即可转移,每次遇到一个 \(0\) 时根据 \(j\) 判断这个 \(0\) 的真实值。考虑优化,我们发现每次 \(t\) 中从某个 \(1\) 跳到 \(0\) 后,这个 \(0\) 到下一个 \(1\) 间的 \(0\) 的个数就是 \(j\),于是我们可以考虑直接在这些 \(0\) 之间跳,就不用记 \(j\) 这一维了。

考虑正着扫,设 \(f_i\) 表示以 \(i\) 开头的路径数,每次处理一个 \(0\) 的极长连续段 \([l,r]\)。假设现在求 \(f_i\),我们枚举 \(i\) 开始走了多少个 \(0\) 时再走了个 \(1\) 跳到了 \(l-1\),这样就能求出删了多少个数也能求出下一个 \(0\) 的位置。

具体地,我们枚举一个 \(k\in [l,i+1]\) 表示走了 \([k,i]\)\(0\) 然后走 \(1\) 跳到了 \(l-1\),记 \(lst_i\) 表示上一个满足和下一个 \(1\) 之间有 \(i\)\(0\)\(j\) 是哪个。令 \(x = lst_{r-i+k-l}\),那我们从 \(k\) 跳到 \(x\) 时可以在中间任何一个点停止,或者跳到 \(x\) 后继续跳。注意如果 \(k = i+1\) 说明直接从 \(i+1\) 跳到了 \(l-1\),那么不能在 \(l-1\) 处停止,转移为 \(f_i\larr l-1-x+f_x+[k\le i]\)。最终答案为序列末尾 \(1\) 的个数加上最后一个 \(0\) 开头的路径数。

可以发现 \(f_{i-1}\)\(f_i\) 之间只差 \(k=l\) 这一项,所以可以做到 \(\mathcal{O}(n)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int N = 1e6+5,mod = 998244353;
int f[N],a[N],lst[N],n;
char buf[1<<21],*p1,*p2;
inline int rd()
{
    char c;int f = 1;
    while(!isdigit(c = getchar()))if(c=='-')f = -1;
    int x = c-'0';
    while(isdigit(c = getchar()))x = x*10+(c^48);
    return x*f;
}
inline void rds()
{
    char c;while((c = getchar()) <= ' ');
    a[n = 1] = c-'0';
    while((c = getchar()) > ' ')a[++n] = c-'0';
}
int main()
{
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    for(int t = rd();t--;)
    {
        rds();
        for(int i = 0;i <= n;i++)lst[i] = f[i] = 0;
        for(int i = 1,sh = 1;i <= n;i++)
            if(a[i])sh = i+1;
            else if(i == n||a[i+1])
            {
                int x = lst[i-sh+1],sum = sh-1-x+f[x];
                for(int j = sh;j <= i;j++)
                    x = lst[i-j],f[j] = ((sum += sh-x+f[x]) %= mod),lst[i-j] = j;
            }
        int s = n;while(s&&a[s])s--;
        printf("%d\n",(n-s+f[s])%mod);
    }
    return 0;
}

CF2048I1 Kevin and Puzzle (Easy Version)

给定一个长度为 \(n\) 的由 LR 组成的字符串 \(s\),你需要构造一个非负整数序列 \(a_i\),对于每个 \(1\le i\le n\) 满足:

  • 如果 \(s_i = L\),那么 \(a_1,\ldots,a_{i-1}\) 中有 \(a_i\) 个不同的数字。
  • 如果 \(s_i = R\),那么 \(a_{i+1},\ldots,a_n\) 中有 \(a_i\) 个不同的数字。

或者报告无解。\(n\le 2\times 10^5\)

我们可以分情况讨论 \(s_1,s_n\) 分别是什么:

  • 如果 \(s_1 = L,s_n = R\),那么一定有 \(a_1 = a_n = 0\),因为 \(a_2,\ldots,a_{n-1}\) 一定 \(\ge 1\),所以可以将 \(1,n\) 去掉,求出 \([2,n-1]\) 这个子问题的答案,然后再将 \(a_2,\ldots,a_{n-1}\) 加一即可。
  • 如果 \(s_1 = s_n = L\),有 \(a_1 = 0\),设 \(a_n = x\),如果 \(a_2,\ldots,a_{n-1}\) 中出现了 \(> x\) 的数,那么一定不合法,因为一定能推出 \(a_n > x\)。如果出现了某个 \(a_i = x\),如果 \(s_i = R\),那么说明 \(a_{i+1},a_n\) 中一定出现了 \(1\sim x\)(因为 \(x\) 是最大值),那么 \(a_1,\ldots,a_{n-1}\) 一定出现了 \(1\sim x\),则 \(a_n\) 不合法。如果 \(s_i = L\),找到最左边的这样的 \(i\),那么说明 \(a_1,\ldots,a_{i-1}\) 中出现了 \(0\sim x-1\)\(a_n\) 也不合法。所以一定是 \(a_2,\ldots,a_{n-1}\) 中出现了 \(1\sim x-1\) 中的所有数。同理,我们求出 \([2,n-1]\) 这个子问题,然后再加一。
  • \(s_1 = s_n = R\),同上。
  • \(s_1 = R,s_n = L\),将 \(a_i\) 全部赋值为 \(1\) 即可。

发现上面都可以递归到子问题,不合法的情况为前面出现了一个 \(LL\)\(RR\),然后中间某一步为 \(RL\),那么我们要求中间要出现 \(0\sim x-2\) 的所有数,但是如果是 \(RL\) 所有数都 \(\ge 1\),所以就无解。复杂度 \(\mathcal{O}(n)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int N = 2e5+5,inf = -1e9;
int a[N],n;bool flag;
char s[N];
int solve(int l,int r,int v)
{
    if(l > r)return -1;
    if(l == r)return a[l] = v,0;
    if(s[l] == 'R'&&s[r] == 'L')
    {for(int i = l;i <= r;i++)a[i] = v+1;return inf;}
    int x = solve(l+1,r-1,v+1);
    if(s[l] == 'L'&&s[r] == 'L')return flag &= x!=inf,a[l] = v,a[r] = v+x+2,x+2;
    if(s[l] == 'R'&&s[r] == 'R')return flag &= x!=inf,a[r] = v,a[l] = v+x+2,x+2;
    return a[l] = a[r] = v,x==inf?inf:x+1;
}
char buf[1<<21],*p1,*p2;
inline int rd()
{
    char c;int f = 1;
    while(!isdigit(c = getchar()))if(c=='-')f = -1;
    int x = c-'0';
    while(isdigit(c = getchar()))x = x*10+(c^48);
    return x*f;
}
inline char gc()
{char c;while((c = getchar()) <= ' ');return c;}
int main()
{
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    for(int t = rd();t--;)
    {
        n = rd();flag = 1;
        for(int i = 1;i <= n;i++)s[i] = gc();
        solve(1,n,0);
        if(!flag)puts("-1");
        else for(int i = 1;i <= n;i++)printf("%d%c",a[i]," \n"[i==n]);
    }
    return 0;
}
posted @ 2025-11-05 20:38  max0810  阅读(17)  评论(1)    收藏  举报