Atcoder Regular Contest 151(A~D)


第一次 VP ARC,赛时很快切了 AB,CD 性质推了一大半,但就是向下推不下去,最终也只是过了 AB。还是思维题做的太少了,总体来说打的不是很好,以后要多练。

补题时补掉了 CD。


赛时

A 一眼贪心,考虑对于两个串相同的部分,无论怎么构造,对于答案显然无贡献;对于不同的部分,显然要一半跟 S 相同,一般跟 T 相同,才能保证构造合法。

这就意味着当 S,T 不同位数的个数为奇数的时候,一定无法构造;否则我们会得到和 \(S,T\) 不同位数的个数,于是从高位到低位贪心,对于相同的部分,我们显然会在这一位放 \(0\);对于不同的部分,我们显然要尽量和该位为 \(0\) 的那个串保持一致,由于此时若已经达到个数上限,则和另一个串保持一致。最终构造出来的解显然合法。

赛时甚至玄学 Wa 了两发,10min 才调完,不太懂,可能是老了。

B 题性质很好推,由于排列的优先级是高位优先,高位相同时比较低位,所以我们不妨从高位到低位开始枚举。对于第 \(i\) 位,我们会比较 \(A_{p_i}\)\(A_i\) 的大小关系,如果前者严格大于后者,后面就不用比了,无论取何值方案都会满足要求。

那么严格大于的方案数是多少呢?\(m\) 个取值里面选两个,大的分给 \(A_{p_i}\),小的分给 \(A_i\),方案数为 \(C_{m}^2\) 与其他 \(A\) 的取值方案数 \(M\) 之积。

而如果前者等于后者,我们还需要接着比较后一位。此时会有一个问题,如果我们在前面已经限制了两个值相等,后面再遇到相等的两个值的时候显然就不会有严格大于的方案数了,而等号具有传递性,所以我们可以用一个并查集来记录哪些值相等,如果前面增加了约束,那么就将约束的两个值合并,比较每一位时判断两个值是否在一个联通块内即可。

除此之外,并查集还可以帮助我们动态维护 \(M\) 的值,于是这道题就做完了。

C 的模型很明显,初始放置的 \(0\)\(1\) 很明显地将数轴分成了若干独立的子段,使我们想到可以将【两侧的数值】和【子段的长度】作为该子段的两个要素,变成一个子游戏,那么整个游戏的结果就是所有子游戏合并之后的结果。

不过赛时将【相邻两个值不能相同】看成了【相邻两个值不能不同】,写完之后疯狂 WA 然后自闭(

D 赛时像个傻子一样一直在考虑有没有数据结构能做到快速合并两个数组,企图将每一个位的 \(0/1\) 作为虚拟节点向该位为 \(0/1\) 的数值连边,维护一个奇怪的树形结构,但是无法快速转移;好像也想到了矩阵,但是没有细想。

于是中间 推+摆烂,最后只过了AB QAQwtcl。


赛后

将 C 读对题之后,尝试将【左右数值不同】和【左右数值相同】以及【一侧有数值一侧无数值】作为三种类型不同的游戏,把他们的 SG 函数打了个表,于是一切明了:当长度为正整数时,数值不同时 SG 函数恒为 \(0\),数组相同时 SG 函数恒为 \(1\),若一侧有值一侧没值,则 SG 函数为子段长度。

于是将所有子游戏的 SG 函数异或起来,如果为 \(0\) 则后手必胜,否则先手必胜。

除此之外,还有一种特殊情况需要考虑:如果初始棋盘上一个值也没有,由于棋盘对称,长度为偶数时后手必胜——后手只需要在先手对称的位置放相反的数字即可;长度为奇数时先手必胜——先手只需要先在正中间放一个值,之后下后手的对称位置即可。

D 题果然是矩阵,有一个奇妙但也很正常的结论是,如果操作的 \(x\) 值不同,则这两个操作相对独立,即交换它们的顺序后最终数组的值不变。手推很好推,可以去简单证明一下。除此之外,对于相同的 \(x\) 值,总会有一对、两个值互相给贡献,有点类似于递推转移方程。

所以我们可以将每一种 \(x\) 值的操作都单独摘出来,并将每一个操作都写成一个矩阵,对于每一种 \(x\) 值的所有矩阵进行相乘,快速转移。最后对于每一种 \(x\) 值的最终矩阵手动模拟即可。

时间复杂度 \(O(2^3Q+2^nn)\)。感觉第一个性质很关键,思考了好久。

D 题核心代码:

n=read(),q=read(),m=(1<<n);
for (int i=0;i<m;i++) a[1][i]=read();
for (int i=0;i<n;i++) S[i].init();//初始化成单位矩阵
for (int i=1;i<=q;i++){
	int x=read(),y=read();
	I.init();
	I.b[y][y^1]=1;
	S[x]=S[x]*I;
}
for (int i=0;i<n;i++){
	for (int j=0;j<m;j++){
		bool x=(j&(1<<i))!=0;
		a[i&1][j]=(S[i].b[x][x]*a[(i&1)^1][j]%mod+S[i].b[x^1][x]*a[(i&1)^1][j^(1<<i)]%mod)%mod;
	}
}
for (int i=0;i<m;i++)
	cout<<a[(n-1)&1][i]<<" ";

E 就懒得看了(

posted @ 2022-10-25 20:27  ydtz  阅读(60)  评论(0编辑  收藏  举报