【AtCoder】AtCoder Grand Contest 041 解题报告(开坑之时已至,目前只有$A,B,C$)
作者:互联网
这里只是开个坑,然后把已经写掉的\(A,B,C\)题解先水一水,剩下的题目打算到时候再做吧。
\(A\):Table Tennis Training(点此看题面)
大致题意: 有\(n\)个位置,一开始一对好朋友分别在\(a,b\)两个位置。每单位时间,二人可分别选择向左或向右走\(1\)个位置(若超出范围则保持不动),求二人至少要多少时间相遇。
不妨设\(a<b\),则可以进行如下分类讨论。
\(b-a\)为偶数
显然,此时二人只要向着对方走,只需\(\frac{b-a}2\)的时间即可相遇。
\(b-a\)为奇数
显然,此时必然是一个人走到\(1\)或\(n\),在那里等一回合使得二人间距离为偶数,然后再走到一起。
而且必然是\(a\)走到\(1\)或者\(b\)走到\(n\),因此我们再分类讨论:
- \(a\)走到\(1\):\(a\)走到\(1\)并等待一回合共需要\(a\)的时间,此时\(b\)走到了\(b-a\),因此还需\(\frac{b-a-1}2\)的时间。总时间为\(a+\frac{b-a-1}2\)。
- \(b\)走到\(n\):\(b\)走到\(n\)并等待一回合共需要\(n-b+1\)的时间,此时\(a\)走到了\(n-b+1+a\),因此还需\(\frac{b-a-1}2\)的时间。总时间为\((n-b+1)+\frac{b-a+1}2\)。
综上所述,最短时间应为\(min(a,n-b+1)+\frac{b-a+1}2\)。
提交记录
神奇地\(CE\)了两发。。。(似乎是选错了语言,好像不能选Clang?)
然后一发就过了。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define LL long long
#define swap(x,y) (x^=y^=x^=y)
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
LL n,a,b;
int main()
{
scanf("%lld%lld%lld",&n,&a,&b);if(a>b&&swap(a,b),(a&1)==(b&1)) return printf("%lld",b-a>>1),0;//使a<b;处理偶数情况
return printf("%lld",(b-a-1>>1)+min(a,n-b+1)),0;//处理奇数情况
}
\(B\):Voting Judges(点此看题面)
大致题意: 有\(n\)个数,你需要进行\(m\)次操作,每次选择恰好\(v\)个数各加\(1\)。求有多少个数在\(m\)次操作后可能成为前\(p\)大的数。
可二分性≠要用二分做
显然这道题是具有可二分性的,因为如果一个数可以,那么比它大的数一定都可以。
但我们一定要用二分吗?事实上,我们直接枚举判断,也可以做到\(O(n)\)。
因此,具有可二分性的题目,不一定就要用二分做。
如何判断
考虑将所有数从大到小排序,设为\(a_{1\sim n}\),并令\(s_{1\sim n}\)为其前缀和。
假设我们当前判断\(a_i\)是否可能成为前\(p\)大的数,那么按照贪心的思想,我们只要让它刚好卡在第\(p\)个就可以了。
因此,我们可以把第\(i\)个数、最大的\(p-1\)个数(因为这\(p-1\)个数是无须超越的)、比\(i\)小的\(n-i+1\)个数(因为这些数怎么加都超不过第\(i\)个数)全都加上\(m\)。
则还剩下需要加的\(1\)的个数为:
\[max(v-p-(n-i),0)\times m \]
我们考虑求出把第\(p\sim i-1\)个数全都刚好加到\(a_i+m\),如果加不到或恰好加满说明当前答案可行,若有\(1\)剩余说明不可行。
那么需要多少个\(1\)呢?这时候前面记下的前缀和就派上用场了,需要的\(1\)的个数就是:
\[(i-p)\times (a_i+m)-(s_{i-1}-s_{p-1}) \]
似乎我们只要比较这个式子与前面式子的大小关系就能得出答案了,但这其实是有瑕疵的。
为什么呢?因为假如你有一个数原本就大于\(a_i+m\),难道你还能给它负数个\(1\),让它变小吗?
所以,我们要先判一下\(a_i+m\)是否大于等于\(a_p\),然后再进行上述判断。(闪指导就被这个坑了)
提交记录
第一发交完等待评测的时候,突然发现有个地方漏开\(long\ long\)了。
于是第一发果不其然\(WA\)了,开了\(long\ long\)重交了一发才过。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define LL long long
using namespace std;
int n,m,v,p,a[N+5];LL s[N+5];
I bool cmp(CI x,CI y) {return x>y;}//从大到小排序
int main()
{
RI i;for(scanf("%d%d%d%d",&n,&m,&v,&p),i=1;i<=n;++i) scanf("%d",a+i);
for(sort(a+1,a+n+1,cmp),i=1;i<=n;++i) s[i]=s[i-1]+a[i];//排序后求前缀和
for(i=n;i>p;--i) if(a[i]+m>=a[p])//枚举答案,首先要使得加上m后大于等于第p个数
if(1LL*max(v-p-(n-i),0)*m<=1LL*(a[i]+m)*(i-p)-(s[i-1]-s[p-1])) break;//用1去填平中间的坑
return printf("%d",i),0;//输出答案(若始终没break则最后i恰好等于p)
}
\(C\):Domino Quality(点此看题面)
大致题意: 有一张\(n\times n\)的网格图,你要摆上至少一个\(1\times 2\)的骨牌,使得各行各列覆盖其至少一格的骨牌个数皆相等。
注意:接下来开始的三部分都只是我的思路历程(勿喷),此题终题解只需看总结部分即可。
手玩一小时
这种构造题一般套路想想都是手玩->发现规律->切掉,怀着这样的想法,我就开始了暗无天日的手玩。。。
于是,大约一小时过去了,弱小的我和强大的闪指导依旧只画出了\(n=3,4,5\)的解。
而且,没有任何规律。。。
题解的辅助
没办法,只好去找了篇题解,看了看最终的规律似乎是:
- 对于\(n=3,5,7\),直接打表。
- 对于\(n\)为偶数,若\(n=4\)则打表,否则规律显然。
- 对于\(n\)为奇数,补一个\(n=5\)的答案转化为\(n\)为偶数。
于是我们从中得到了几点启发:
- 难怪我们没有找到任何规律,因为我们求出的这几个答案都是特殊情况。。。
- 我们还需要手玩出\(n=7\)并发现\(n\)为偶数的规律,再去考虑怎么补上\(n=5\)的答案把奇数变成偶数,这又将是一个浩大的工程。
画去画去就困了,先睡个觉,明天再接着画。
自习课加成
果然,正如闪指导所言,在班里就能智商++,自习课更是有着极为优秀的加成。
第二天起来到班里,来机房前就无聊乱画(说起来今明两天是月考,因为停课逃掉了),结果突然就把\(n\)为偶数情况的规律画出来了?!
此时极为亢奋的我再接再厉,不到一分钟又画出了\(n=7\)?!
(顺便说一下,过于亢奋的我刚画完就兴冲冲地奔向了机房,到了之后发现画了图的草稿纸没带,结果凭借残余的记忆还又用了五六分钟才重新画出\(n=7\)的解。由此再次验证了闪指导的话的正确性,果然闪指导是我们的红太阳,他说的话都是真理!)
总结
好了,上面那一堆都是废话,接下来才是正经的结论。
-
对于\(n=3,5,7\),直接打表。(可见代码)
-
对于\(n\)为偶数,我的确是自己推出了一个统一的规律,但感觉把\(n\)分类为模\(6\)余\(0,2,4\)三种情况似乎更好理解、说明,也更好码(码量虽然大了点,但实际只是多按了几个\(Ctrl\ C+Ctrl\ V\)):
\(n\%6=0\),
如图所示:(由于我比较懒,图被吞了,直接用题目中的表示方法吧)x.aa......z. x.bb......z. y.cc......y. y...aa....y. z...bb....x. z...cc....x. .x....aa...z .x....bb...z .y....cc...y .y......aa.y .z......bb.x .z......cc.x
我想图画出来后这规律应该就比较显然了吧。。。
\(n\%6=2\),
如图所示:x.aa.......p.. x.bb.......p.. y..cc.......z. y...aa......z. z...bb......y. z....cc.....y. .x....aa....x. .x....bb....x. .y.....cc....z .y......aa...z .z......bb...y .z.......cc..y ..p.......aa.x ..p.......bb.x
和\(n\%6=0\)很像,不是吗?事实上,仔细观察就发现仅仅是把所有"cc"右移了一格(可理解为"pp"的存在占据了"cc"原本的位置)。
\(n\%6=4\),
如图所示:x.aa.........q.. x..bb........q.. y..cc........p.. y...aa.......p.. z....bb.......z. z....cc.......z. .x....aa......y. .x.....bb.....y. .y.....cc.....x. .y......aa....x. .z.......bb....z .z.......cc....z ..p.......aa...y ..p........bb..y ..q........cc..x ..q.........aa.x
看完前两种情况就很好明白了,"pp"和"qq"占据了"bb"和"cc"原本的位置,使得"bb"和"cc"需要右移一格。
-
对于\(n\)为奇数,我们需要这样画:
**********..... **********..... **********..... **********..... **********..... **********..... **********..... **********..... **********..... **********..... ..........abba. ..........a..ab ..........bba.b ..........a.acc ..........abbaa
其中"*"构成的矩阵表示\(n-5\)的答案,右下角为\(5\)的答案。
大体上就是这样了,然后就是模拟模拟模拟。
提交记录
\(RE\)???仔细看了一遍代码,然后发现智障地写了这样一句:
return puts("-1");
\(WA\)???而且只\(WA\)一个点???
不断思索有什么单个数据是特殊的,最后发现挂在了\(9\)上(\(5+4\)),\(9\)中的\(4\)也是需要特判的,而我原本是把\(4\)单独拉出来特判的。所以只好又把\(4\)的情况放回了偶数大家庭。。。
然后终于过了。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000
using namespace std;
int n,a[N+5];char s[N+5][N+5];
char s3[4][4]={"","aab","b.b","baa"};
char s5[6][6]={"","abba.","a..ab","bba.b","a.acc","abbaa"};
char s7[8][8]={"","aabbaa.","b..a..a","b..a..a","...baab","...bccb","abb...a","acc...a"};
char s4[5][5]={"","aaba","ccba","abcc","abaa"};
I void Draw(CI n)//模拟看起来很长,实际上仔细看下就发现三部分几乎一样,只有小变动而已(实际上我本来就基本上是复制粘贴的)
{
RI i,j;if(n==4)//特判n=4
{
for(i=1;i<=4;++i) for(j=1;j<=4;++j) s[i][j]=s4[i][j-1];return;
}
if(n%6==0)
{
for(i=1;i<=n/6;++i)
s[6*i-5][i]=s[6*i-4][i]=s[n-6*i+6][n-i+1]=s[n-6*i+5][n-i+1]='x',
s[6*i-3][i]=s[6*i-2][i]=s[n-6*i+4][n-i+1]=s[n-6*i+3][n-i+1]='y',
s[6*i-1][i]=s[6*i][i]=s[n-6*i+2][n-i+1]=s[n-6*i+1][n-i+1]='z';
for(i=1;i<=n/3;++i)
s[3*i-2][n/6+2*i-1]=s[3*i-2][n/6+2*i]='a',
s[3*i-1][n/6+2*i-1]=s[3*i-1][n/6+2*i]='b',
s[3*i][n/6+2*i-1]=s[3*i][n/6+2*i]='c';
}
if(n%6==2)
{
for(i=1;i<=n/6;++i)
s[6*i-5][i]=s[6*i-4][i]=s[n-6*i+6][n-i+1]=s[n-6*i+5][n-i+1]='x',
s[6*i-3][i]=s[6*i-2][i]=s[n-6*i+4][n-i+1]=s[n-6*i+3][n-i+1]='y',
s[6*i-1][i]=s[6*i][i]=s[n-6*i+2][n-i+1]=s[n-6*i+1][n-i+1]='z';
s[n-1][i]=s[n][i]=s[2][n-i+1]=s[1][n-i+1]='p';
for(i=1;i<=n/3;++i)
s[3*i-2][n/6+2*i-1]=s[3*i-2][n/6+2*i]='a',
s[3*i-1][n/6+2*i-1]=s[3*i-1][n/6+2*i]='b',
s[3*i][n/6+2*i]=s[3*i][n/6+2*i+1]='c';
s[n-1][n/6+2*i-1]=s[n-1][n/6+2*i]='a',
s[n][n/6+2*i-1]=s[n][n/6+2*i]='b';
}
if(n%6==4)
{
for(i=1;i<=n/6;++i)
s[6*i-5][i]=s[6*i-4][i]=s[n-6*i+6][n-i+1]=s[n-6*i+5][n-i+1]='x',
s[6*i-3][i]=s[6*i-2][i]=s[n-6*i+4][n-i+1]=s[n-6*i+3][n-i+1]='y',
s[6*i-1][i]=s[6*i][i]=s[n-6*i+2][n-i+1]=s[n-6*i+1][n-i+1]='z';
s[n-3][i]=s[n-2][i]=s[4][n-i+1]=s[3][n-i+1]='p',
s[n-1][i]=s[n][i]=s[2][n-i+1]=s[1][n-i+1]='q';
for(i=1;i<=n/3;++i)
s[3*i-2][n/6+2*i-1]=s[3*i-2][n/6+2*i]='a',
s[3*i-1][n/6+2*i]=s[3*i-1][n/6+2*i+1]='b',
s[3*i][n/6+2*i]=s[3*i][n/6+2*i+1]='c';
s[n][n/6+2*i-1]=s[n][n/6+2*i]='a';
}
}
int main()
{
RI i,j;if(scanf("%d",&n),n==2) return puts("-1"),0;//n=2输出-1
if(n==3) {for(i=1;i<=3;++i) puts(s3[i]);return 0;}//特判n=3
if(n==5) {for(i=1;i<=5;++i) puts(s5[i]);return 0;}//特判n=5
if(n==7) {for(i=1;i<=7;++i) puts(s7[i]);return 0;}//特判n=7
for(i=1;i<=n;++i) for(j=1;j<=n;++j) s[i][j]='.';//初始化点阵
if(n&1) for(Draw(n-5),i=1;i<=5;++i) for(j=1;j<=5;++j) s[n-5+i][n-5+j]=s5[i][j-1];else Draw(n);//奇数转化为偶数,然后开始模拟画画
for(i=1;i<=n;++i) puts(s[i]+1);return 0;//输出答案
}
标签:.....,AtCoder,bb,..,Contest,cc,....,开坑,define 来源: https://www.cnblogs.com/chenxiaoran666/p/AtCoderAGC041.html