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\) 列的,最终答案为:
复杂度为 \(\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;
}