sb atcoder 提前比赛时间/fn/fn/fn……sb atcoder 还我 rating/zk/zk/zk

A

签到题,枚举位数 \(+\) 前导 \(1\) 个数然后随便算算贡献即可,时间复杂度 \(\log^2_{10}(n)\)

B

u1s1 这个 B 感觉比一般的 B 难不少啊……

考虑贪心。首先第一位我们随便钦定 \(n\) 个 \(0\),\(n\) 个 \(1\) 和 \(n\) 个 \(2\),那么后面的位我们肯定会贪心地让第一位填 \(2\) 的都填 \(0\),第一位填 \(0/1\) 的自然不用管它们因为我们后面自然有办法为它们填数,但是这样会导致字符串出现重复的情况,怎么解决呢?如果我们发现目前不能区分出来的字符串个数 \(>3^{l-i}\),其中 \(i\) 表示当前填到了第几位,那么我们就贪心地依次填上 \(0000...0,000...1,\cdots,1111...11\),然后把最后的零头放到那“不能被区分出来的字符串集合”中即可。

可能讲得有点玄乎,看代码可能会比较好理解。

const int MAXN=1.5e5;
int n,l,pw3[18];
string s[MAXN+5];
int main(){
scanf("%d%d",&n,&l);
for(int i=(pw3[0]=1);i<=l;i++) pw3[i]=pw3[i-1]*3;
vector<int> id;
for(int i=1;i<=n;i++) id.pb(i);int cur=n;
for(int i=1;i<=n;i++) s[i].pb('0'),s[i+n].pb('1'),s[i+n+n].pb('2');
for(int _=2;_<=l;_++){
cur=id.size();
if(cur<=pw3[l-_]){
for(int x:id) s[x].pb('2'),s[x+n].pb('1'),s[x+n+n].pb('0');
} else if(cur<=pw3[l-_]*2) {
for(int i=0;i<pw3[l-_];i++){
int x=id[i];
// printf("%d\n",x);
s[x].pb('2');s[x+n].pb('1');s[x+n+n].pb('0');
for(int j=0;j<l-_;j++){
s[x].pb('0'+(i/pw3[j]%3));
s[x+n].pb('0'+(i/pw3[j]%3));
s[x+n+n].pb('0'+(i/pw3[j]%3));
}
} vector<int> nw;
for(int i=pw3[l-_];i<id.size();i++){
int x=id[i];
// printf("%d\n",x);
s[x].pb('0');s[x+n].pb('2');s[x+n+n].pb('1');
nw.pb(x);
} id=nw;
} else {
for(int i=0;i<pw3[l-_];i++){
int x=id[i];
s[x].pb('2');s[x+n].pb('1');s[x+n+n].pb('0');
for(int j=0;j<l-_;j++){
s[x].pb('0'+(i/pw3[j]%3));
s[x+n].pb('0'+(i/pw3[j]%3));
s[x+n+n].pb('0'+(i/pw3[j]%3));
}
}
for(int i=pw3[l-_];i<pw3[l-_]*2;i++){
int x=id[i];
s[x].pb('0');s[x+n].pb('2');s[x+n+n].pb('1');
for(int j=0;j<l-_;j++){
s[x].pb('0'+(i/pw3[j]%3));
s[x+n].pb('0'+(i/pw3[j]%3));
s[x+n+n].pb('0'+(i/pw3[j]%3));
}
} vector<int> nw;
for(int i=pw3[l-_]*2;i<id.size();i++){
int x=id[i];
s[x].pb('1');s[x+n].pb('0');s[x+n+n].pb('2');
nw.pb(x);
} id=nw;
}
}
for(int i=1;i<=n*3;i++) cout<<s[i]<<endl;
return 0;
}

C

请问您见过几个 arc C 被评到 2000+

首先考虑一个非常朴素的做法,我们先事先将 \(X\) 减 \(1\),然后每次从高位到低位一位一位考虑,如果当前 \(X\) 等于零那就直接 break,否则如果当前 \(X\le 2^{n-l}\),其中 \(l\) 为当前考虑的位,那么我们就在这一位上填 \(0\) 并将 \(X\) 减一,否则我们就在这一位上填 \(1\) 并将 \(X\) 减去 \(2^{n-l}\)

然后就是我降智的地方了。由于这题 \(X\) 很大需要高精度,因此我以为直接减 \(1\) 复杂度会退化到平方,于是想着打各种标记之类的奇怪玩意儿,WA 了好几发,事实上直接减 \(1\) 复杂度是正确的,感性理解一下不难发现当你进行一次减 \(1\) 之后退了很多很多位,那么在接下来很长一段时间内就不可能再退这么多位,如果理性证明地大概就 \(\sum\limits_{i=0}^{\log_2(n)}\lfloor\dfrac{n}{2^i}\rfloor=\mathcal O(n)\),因此总时间复杂度也是 \(\mathcal O(n)\)

D

考虑 \(c_i=a_i\oplus b_i\),那么不难发现对于一组 \(i,j\),\(a_i\oplus a_j\) 与 \(b_i\oplus b_j\) 从高到低第一个值不同的位就是 \(c_i\oplus c_j\) 二进制下的 significant bit,下文中称其为第 \(j\) 位,因此 \(\min(a_i\oplus a_j,b_i\oplus b_j)\) 取到 \(a_i\oplus a_j\) 当且仅当 \(a_i\oplus a_j\) 第 \(d\) 位为 \(1\)。

我们再考虑这样一个问题,给定序列 \(a\) 求它们两两异或和的和,01-trie 是一种方法,事实上我赛时一开始也考虑了这个做法,但是有一种更简单的做法是你考虑每一位的贡献,我们对于每一位记录一下有多少个数这一位为 \(0\),有多少个数这一位为 \(1\),这样可以 \(\mathcal O(1)\) 计算每一位的贡献。

接下来考虑原问题。我们将所有 \(c_i\) 插入 01-trie,然后对于 \(c_i\) 每一个为 \(1\) 的位 \(d\),我们在其对应的 01-trie 的节点上开 4 个长度为 \(\log(n)\) 的数组,下标为 \(j\) 的位置分别维护这样几个信息:对于经过这个节点且 \(c_i\) 为 \(1\) 的 \(i\),有多少个:

  • \(a_i\) 第 \(d\) 位为 \(0\) 的 \(i\),满足 \(a_i\) 的第 \(j\) 位为 0/1
  • \(a_i\) 第 \(d\) 位为 \(0\) 的 \(i\),满足 \(b_i\) 的第 \(j\) 位为 0/1
  • \(a_i\) 第 \(d\) 位为 \(1\) 的 \(i\),满足 \(a_i\) 的第 \(j\) 位为 0/1
  • \(a_i\) 第 \(d\) 位为 \(1\) 的 \(i\),满足 \(b_i\) 的第 \(j\) 位为 0/1

这样在遍历 01-trie 计算贡献的过程中,我们考虑计算一个 \(i\) 对答案的贡献时,可以考虑 \(c_i\) 所有为 \(0\) 的位,然后通过预处理的信息在单次 \(\mathcal O(\log n)\) 的时间内计算贡献。

记得加上 \(c_i=c_j\) 的贡献,这一部分的计算是 trivial 的,直接对每一种 \(c_i\) 重复一遍上面弱化版的过程即可。

时间复杂度 \(n\log^2n\)。

const int MAXN=262144;
const int LOG_N=18;
const int MAXP=2097152;
int n,A[MAXN+5],B[MAXN+5],C[MAXN+5];
int ch[MAXP+5][2],cnt[4][MAXP+5][LOG_N+2][2],rt=0,ncnt=0;
vector<int> pc[MAXN+5];
ll res=0;
void insert(int &k,int v,int dep,int a,int b){
if(!k) k=++ncnt;if(!~dep) return;
insert(ch[k][v>>dep&1],v,dep-1,a,b);
if(v>>dep&1){
if(a>>dep&1){
for(int j=0;j<LOG_N;j++) cnt[0][ch[k][v>>dep&1]][j][a>>j&1]++;
for(int j=0;j<LOG_N;j++) cnt[1][ch[k][v>>dep&1]][j][b>>j&1]++;
} else {
for(int j=0;j<LOG_N;j++) cnt[2][ch[k][v>>dep&1]][j][a>>j&1]++;
for(int j=0;j<LOG_N;j++) cnt[3][ch[k][v>>dep&1]][j][b>>j&1]++;
}
}
}
void query(int k,int v,int dep,int a,int b){
if(!k||!~dep) return;
query(ch[k][v>>dep&1],v,dep-1,a,b);
if(~v>>dep&1){
int c=ch[k][~v>>dep&1];
if(!c) return;
// printf("%d %d %d %d %d\n",v,dep,c,a,b);
if(a>>dep&1){
for(int j=0;j<LOG_N;j++) res+=1ll*cnt[0][c][j][~a>>j&1]*(1<<j);
for(int j=0;j<LOG_N;j++) res+=1ll*cnt[3][c][j][~b>>j&1]*(1<<j);
} else {
for(int j=0;j<LOG_N;j++) res+=1ll*cnt[1][c][j][~b>>j&1]*(1<<j);
for(int j=0;j<LOG_N;j++) res+=1ll*cnt[2][c][j][~a>>j&1]*(1<<j);
}
}
}
int cnt0[MAXN+5][LOG_N+2][2];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&A[i]);
for(int i=1;i<=n;i++) scanf("%d",&B[i]),C[i]=A[i]^B[i],pc[C[i]].pb(i);
for(int i=1;i<=n;i++) insert(rt,C[i],LOG_N-1,A[i],B[i]);
for(int i=1;i<=n;i++) for(int j=0;j<LOG_N;j++)
cnt0[C[i]][j][A[i]>>j&1]++;
// for(int i=1;i<=n;i++) printf("%d\n",c[i]);
for(int i=0;i<MAXN;i++){
ll s=0;
for(int id:pc[i]) for(int j=0;j<LOG_N;j++)
s+=1ll*cnt0[i][j][~A[id]>>j&1]*(1<<j);
s>>=1;res+=s;
}
for(int i=1;i<=n;i++) query(rt,C[i],LOG_N-1,A[i],B[i]);
printf("%lld\n",res);
return 0;
}

E

并没有自己想出来,搬运官方题解 ing

首先考虑如果我们想要尽量让大的数都在最终的 \(s\) 中那该怎么选择每次插入的数。显然按 \(1,2,3,\cdots,A\) 的顺序插入肯定是最优的。因此我们假设 \(b_1,b_2,\cdots,b_{A-B}\) 为按 \(1,2,3,\cdots,A\) 顺序插入后剩余的数组成的集合,其中 \(b_i<b_{i+1}\)。

我们再假设 \(c_1,c_2,\cdots,c_{A-B}(c_i<c_{i+1})\) 为按某种顺序插入后剩余的数组成的集合,那么我们不妨猜测 \(c_i\le b_i(i\in[1,A-B])\)。我们假设 \(p_1,p_2,\cdots,p_{B}\) 为按 \(1,2,3,\cdots,A\) 中不在剩余集合中的数组成的集合,\(q_1,q_2,\cdots,q_B\) 为按 \(c_1,c_2,\cdots,c_{A-B}\) 对应的顺序插入后不在剩余集合中的数组成的集合,其中 \(p_i<p_{i+1},q_i<q_{i+1}\),然后对于第 \(i\) 次插入考虑这样构造:

  • 如果 \(\exists j,s.t.b_j=i\),那么我们此次插入中插入 \(c_j\),其中 \(b_j=i\)。
  • 否则必然 \(\exists j,s.t.p_j=i\),我们就插入 \(q_j\)。

不难证明最后得到的数就是 \(c_1,c_2,\cdots,c_{A-B}\)。因此 \(c_1,c_2,\cdots,c_{A-B}\) 可以作为最终集合存在的充要条件是 \(\forall i\in[1,A-B],c_i\le b_i\)。到这里事情就变得很 trivial 了。\(dp_{i,j}\) 表示考虑了前 \(i\) 个数,\(c_i=j\) 的合法的 \(c\) 的个数,前缀和优化 DP 即可,时间复杂度 \(A^2\)。

AtCoder Regular Contest 127 题解的更多相关文章

  1. AtCoder Regular Contest 103 题解

    C-/\/\/\ #include<algorithm> #include<iostream> #include<cstdlib> #include<ioma ...

  2. AtCoder Regular Contest 127

    Portal B Description 给出\(n(\leq5\times10^4),L(\leq15)\),构造\(3n\)个不同\(L\)位的三进制数,使得在这\(3n\)个数的每一位上,0/1 ...

  3. Atcoder Regular Contest 123 题解

    u1s1 我是真的不知道为什么现场这么多人切了 D,感觉 D 对思维要求显然要高于其他 300+ 人切掉的 D 吧(也有可能是 Atc 用户整体水平提升了?) A 开 幕 雷 击(这题似乎 wjz 交 ...

  4. AtCoder Regular Contest 094 (ARC094) CDE题解

    原文链接http://www.cnblogs.com/zhouzhendong/p/8735114.html $AtCoder\ Regular\ Contest\ 094(ARC094)\ CDE$ ...

  5. AtCoder Regular Contest 096

    AtCoder Regular Contest 096 C - Many Medians 题意: 有A,B两种匹萨和三种购买方案,买一个A,买一个B,买半个A和半个B,花费分别为a,b,c. 求买X个 ...

  6. AtCoder Regular Contest 061

    AtCoder Regular Contest 061 C.Many Formulas 题意 给长度不超过\(10\)且由\(0\)到\(9\)数字组成的串S. 可以在两数字间放\(+\)号. 求所有 ...

  7. AtCoder Regular Contest 092

    AtCoder Regular Contest 092 C - 2D Plane 2N Points 题意: 二维平面上给了\(2N\)个点,其中\(N\)个是\(A\)类点,\(N\)个是\(B\) ...

  8. AtCoder Regular Contest 093

    AtCoder Regular Contest 093 C - Traveling Plan 题意: 给定n个点,求出删去i号点时,按顺序从起点到一号点走到n号点最后回到起点所走的路程是多少. \(n ...

  9. AtCoder Regular Contest 094

    AtCoder Regular Contest 094 C - Same Integers 题意: 给定\(a,b,c\)三个数,可以进行两个操作:1.把一个数+2:2.把任意两个数+1.求最少需要几 ...

随机推荐

  1. Java版人脸检测详解上篇:运行环境的Docker镜像(CentOS+JDK+OpenCV)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  2. [对对子队]事后总结Beta

    设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 要做一个游戏,定义的很清楚,实现出来的效果贴近定义,对用户和场景有清晰描述 我们达到目标了么(原计划的功 ...

  3. mongodb的聚合操作

    在mongodb中有时候我们需要对数据进行分析操作,比如一些统计操作,这个时候简单的查询操作(find)就搞不定这些需求,因此就需要使用  聚合框架(aggregation) 来完成.在mongodb ...

  4. CSP-S2021幽寂

    不管怎么说,这次比赛考的比这一段时间以来的模拟赛都难看 难受,但是也不想太表现出来,所以更难受.... 有点害怕会退役...... Day -6 前一天晚上回宿舍的时候和\(zxs\)一路,聊的过程中 ...

  5. 从0到1使用Kubernetes系列(五):Kubernetes Scheduling

    前述文章介绍了Kubernetes基本介绍,搭建Kubernetes集群所需要的工具,如何安装,如何搭建应用.本篇介绍怎么使用Kubernetes进行资源调度. Kubernetes作为一个容器编排调 ...

  6. (转)linux下execl和system函数

    linux下,system函数和execl函数都是用于执行一条系统命令.今天仔细看了system函数的实现,想找出和execl函数的差别. 这里先进行一些背景知识补充: fork(创建一个新的进程): ...

  7. 从零开始搭建你的nvim ide

    前言概述 vim由于其丰富的扩展性.出色的跨平台性.高效率的操作性深受一大批粉丝的追捧,甚至就连vim和emacs之间孰优孰劣的话题都能被引起一场编辑器之间的圣战,足以见vim是多么的优秀. vim的 ...

  8. 『学了就忘』Linux基础命令 — 19、目录操作的相关命令

    目录 1.ls命令 2.cd命令 (1)绝对路径和相对路径 (2)cd命令的简化用法 3.pwd命令 4.mkdir命令 5.rmdir命令 常用目录操作的相关命令: ls命令 cd命令 pwd命令 ...

  9. 阿里云ECI如何6秒扩容3000容器实例?

    引言 根据最新CNCF报告,有超过90%的用户在生产环境使用容器,并且有超过80%的用户通过Kubernetes管理容器.是不是我们的生产环境上了K8s就完美解决了应用部署的问题?IT界有句俗语,没有 ...

  10. Redis源码分析(adlist)

    源码版本:redis-4.0.1 源码位置: adlist.h : listNode.list数据结构定义. adlist.c:函数功能实现. 一.adlist简介 Redis中的链表叫adlist( ...