Preface
我是若只。
A. Penchick and Modern Monument
先吃三发罚时。
最优策略应当是把所有数都调成众数,然而我一开始就忙着往后面做,胡乱猜了个结论就 WA 了,又猜了一个又 WA 了,再猜了一个再 WA 了。
点击查看代码
const int N=105;
int n,a[N];int main()
{int T; read(T);while(T--){read(n);int con=0,maxcon=0;for(int i=1;i<=n;i++){read(a[i]);if(a[i]==a[i-1]) con++;else con=1;maxcon=max(maxcon,con);}write(n-maxcon,'\n');}return 0;
}
B. Penchick and Satay Sticks
这道题做得稍微细心了些。
从反面思考一下,一个有序序列里任意一个元素最多被交换一次,所以要让一个无序序列通过这种方式变得有序,每个元素也只能被交换最多一次。尝试交换所有可以交换的逆序对,最后判断是否有序即可。
点击查看代码
const int N=2e5+5;
int n,a[N];int main()
{int T; read(T);while(T--){read(n);for(int i=1;i<=n;i++)read(a[i]);for(int i=2;i<=n;i++)if(a[i-1]-a[i]==1) swap(a[i-1],a[i]);puts(is_sorted(a+1,a+n+1)?"YES":"NO");}return 0;
}
C. Penchick and BBQ Buns
从这里开始失衡。
偶数情况很简单,两个两个放就可以了,重点是奇数情况。
首先,如果要放奇数个,一定有至少一个数出现了奇数次。
其次,如果这个数出现了超过三次,因为放三个的约束条件小于放更多个,所以可以把多出来的那些给换成别的成对的数。
那么情况就转化成了如何放下三个同一个数。一旦放下同一个数三次,后面的就可以直接按照偶数的方法无脑放,所以考虑如何在最短的长度内放满 \(3\) 个同样的数。
设这个数三次分别出现在位置 \(p,p+X,p+X+Y\),根据题目要求,\(X\) 和 \(Y\) 是平方数,且 \((X+Y)\) 也是平方数,所以设 \(x^2=X,y^2=Y,z^2=X+Y\),那么有 \(x^2+y^2=z^2\)。
这一段的长度为 \(z^2+1\),而使其最小的 \(z=5\),对应的 \(x=3,y=4\),所以思考如何向下面的数列中填数:
1 _ _ _ _ _ _ _ _ 1 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 1
前一半有 \(8\) 个空,按照偶数方法填入;后一半有 \(15\) 个空,偶数方法填不完,但是剩下的一个可以考虑组合成距离 \(4\):
1 2 2 3 3 4 4 5 5 1 6 6 7 7 8 8 9 9 10 10 12 11 11 1 12
这样,只用 \(27\) 个数就可以放入三个相同的数,后面按照偶数方法加即可得到任意奇数,前面的无解(\(26\) 已经是放三个的理论最小长度)。
然后就做出来了:
int main()
{int T; read(T);while(T--){int n; read(n);if(n&1){if(n<27) puts("-1");else{printf("1 2 2 3 3 4 4 5 5 1 6 6 7 7 8 8 9 9 10 10 12 11 11 1 12 ");for(int i=1;i<=(n-27)>>1;i++)write(i+14,' '),write(i+14,' ');putchar('\n');}}else{for(int i=1;i<=n>>1;i++)write(i,' '),write(i,' ');putchar('\n');}}return 0;
}
接下来分析我考试的时候到底在想什么:
开题,前几题做得有点久,这道题要加速了。
然后想当然地在算的时候把距离算作元素数量,构造出了 1 2 2 1 3 3 1
,WA 第一发。
欸,怎么 WA 了?再看题,原来距离是两点之间间隔的点数啊(你没看错,我确实又理解错了),然后构造出 1 2 2 2 2 1 3 3 3 3 1
,WA 第二发。
又 WA?哦,原来是连续四个用了相同的数啊(此时的我还没有意识到问题的严重性),改一下就好了,1 2 2 3 3 1 4 4 5 5 1
,WA 第三发。
还 WA?不对不对不对,我再仔细看看……
然后我仔细重读了一遍题面,终于把题意搞明白了(此时距开题以过去 \(30\) 分钟)。又写了一个简单的 check 程序用来检查答案是否合法。
然后我想到了 \(3^2+4^2=5^2\) 的构造方式,然而,因为后面一半要填奇数个,把我的思维引入了再放同一个数三次的递归思维陷阱中,导致我认为这样没有合法方案,进而认为两边空位个数有奇数的平方数方案都不合法。
打了个表+粗略证明了一下,发现不存在两边空位都为偶数的平方数方案,然后我认为奇数都不合法,WA 第四发。
然后我想到了只要两边空位奇偶性相同就可以构造,通过这个,我找到了 \(6^2+8^2=10^2\),这是两边空位奇偶性相同情况下的最短可能段,然后我构造了 1 ...×35... 1 ...×63... 1
,交上去,WA 第五发。
逐渐红温,仔细检查确认代码无误后,我确定是算法出了问题。因为这样构造出的方案一定合法,所以只能是我把合法的输入判为不合法才错的。回头看 \(3^2+4^2=5^2\) 的平方数方案,我这才想到在后面加数来与前面凑成距离 \(4\),构造出 1 2 2 3 3 4 4 5 5 1 6 6 7 7 8 8 9 9 10 10 11 11 12 1 13 13 12
从而认定 \(29\) 是最小可能奇数解,WA 第六发
开始怀疑人生,乱改了一下,不出所料,WA 第七发
纵观这 \(7\) 次提交记录,虽然没有红温到改一下交一下的地步(有罚分),除第七次外,每次提交都是有了一定进展后才提交。但是如果仔细分析可以发现:
对于前三次提交,很明显:
- 对自己预期较高 + 比较重视这场比赛 \(\longrightarrow\) 精神比较紧张,判断容易失误
- 前两道题做题时间较长 + 看到排行榜上已经有人完成 C 题 \(\longrightarrow\) 低估此题难度;心态失衡,急于求成,不想深入思考
- WA 了以后太过想当然,对解法过于自信,认为自己 WA 的原因只是因为一些细节问题,没有仔细验证就再次提交 \(\longleftarrow\) 上两条原因都造成了这样的结果
于是这样,buff 叠满,我像若只一样 WA 了三发。
如果深入思考可以发现,急于求成,不想深入思考应该是主要原因,而预期过高导致判断失误是根本原因。偶数的做法我能秒出可以看出我其实是理解了题意的,但是后面的几次错误却又是因为题意理解而出。读题的时候我的想法都还和题目描述一致的,后面就直接把那个公式理解成了“距离”,进而到草稿上就变成了其它稀奇古怪的东西。
所以前三次 WA 是完全可以避免的,而后面也完全不用 WA 那么多次:
因为潜意识里我的精神比较紧张,加之对自己和题目都判断失误,让我多次卡进死胡同而不愿检查算法正确性(比如最开始认为别的数只能往里面的空填)。
综上所述,以下是我需要改正的地方:
- 在任何时候,冷静地思考问题都是最优选择。不要被外界干扰想法。
- 在想出正解之前,不应草率地判定任何一道题的难度,因为难度是因人而异的。
- 检查代码,特别是不严谨的想法算法检查优先级较高,不应该留到最后检查。
最后放一下战绩:
D - Penchick and Desert Rabbit
第一眼:动态规划
第二眼:树状数组优化动态规划(然而打出来发现并不是)
第三眼:图论,拓扑排序,再动态规划(然而全是环,无法拓扑排序)
第四眼:全是环?那不是说每一个连通块都是强联通的咯!并查集维护连通块的同时找块内最大值和最小值,如果当前最大值大于之前的最小值,就可以和前面合并。
#include<cstdio>
#include<algorithm>
using namespace std;namespace IO{
#ifndef JC_LOCAL
const int SIZE=1<<20; char buf[SIZE],*p1=buf,*p2=buf;
#define getchar() ((p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2))?EOF:*p1++)
#endif
template<typename TYPE> void read(TYPE &x)
{x=0; bool neg=false; char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')neg=true;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^'0');ch=getchar();}if(neg){x=-x;} return;
}
template<typename TYPE> void write(TYPE x)
{if(!x){putchar('0');return;} if(x<0){putchar('-');x=-x;}static int sta[55];int statop=0; while(x){sta[++statop]=x%10;x/=10;}while(statop){putchar('0'+sta[statop--]);} return;
}
template<typename TYPE> void write(TYPE x,char ch){write(x);putchar(ch);return;}
} using namespace IO;const int N=5e5+5;
int n,a[N],f[N];namespace Union{int fa[N],mx[N],mn[N];
void Init()
{for(int i=1;i<=n;i++)fa[i]=i,mx[i]=a[i],mn[i]=a[i];return;
}
int query(int x)
{return fa[x]==x?x:fa[x]=query(fa[x]);
}
void merge(int x,int y)
{x=query(x),y=query(y);mx[x]=max(mx[x],mx[y]);mn[x]=min(mn[x],mn[y]);fa[y]=x;return;
}} using namespace Union;pair<int,int> lmax[N];int main()
{int T; read(T);while(T--){read(n);for(int i=1;i<=n;i++)read(a[i]);Init();for(int i=1;i<=n;i++){lmax[i]=max(lmax[i-1],{a[i],i});merge(i,lmax[i].second);}int low=0x3f3f3f3f;for(int i=n;i>=1;i--){if(mx[query(i)]>low) merge(i,i+1);low=min(low,mn[query(i)]);}for(int i=1;i<=n;i++)write(mx[query(i)],' ');putchar('\n');}return 0;
}