码迷,mamicode.com
首页 > 其他好文 > 详细

【AtCoder】AtCoder Grand Contest 041 解题报告(开坑之时已至,目前只有$A,B,C$)

时间:2020-05-13 09:19:47      阅读:58      评论:0      收藏:0      [点我收藏+]

标签:isp   nis   并发   str   line   解题报告   一个人   操作   strong   

点此进入比赛

这里只是开个坑,然后把已经写掉的\(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】AtCoder Grand Contest 041 解题报告(开坑之时已至,目前只有$A,B,C$)

标签:isp   nis   并发   str   line   解题报告   一个人   操作   strong   

原文地址:https://www.cnblogs.com/chenxiaoran666/p/AtCoderAGC041.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!