Codeforces Round 662 赛后解题报告
梦幻开局到1400+的悲惨故事
A. Rainbow Dash, Fluttershy and Chess Coloring
这个题很简单,我们可以画几张图,发现每一次我们染色的最佳方法就是每次往里面多填一圈,并把上一圈给填满。
比如上图就很好地说明了这个过程,大家可以用画一下 \(n=4,n=5,n=6,n=7\),就能验证这个命题了,所以一个 \(n\times n\) 的矩阵有 \(\lfloor\frac{n}{2}\rfloor+1\) 圈,所以直接输出即可。
//Don't act like a loser.
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
//#pragma GCC optimize("Ofast","-funroll-loops","-fdelete-null-pointer-checks")
//#pragma GCC target("ssse3","sse3","sse2","sse","avx2","avx")
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int n;
signed main() {
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
int t=read();
while(t--) {
n=read();
cout<<n/2+1<<endl;
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
B. Applejack and Storages
我们思考一下这个题给我们的信息。我们要用已有木棒拼出一个正方形和一个长方形。因为长方形的范围比正方形广,所以我们有一个贪心策略就是先判断是否有正方形再判断剩下的是否有长方形。
我们做的就是要统计有多少组木棒可以拼出正方形,我们记为 \(four\)(重复的木棒不能出现在两组中)。我们再来统计把所有 \(4\) 个木棒长度相等的组剔除后,有多少组是两个木棒程度相等的,记为 \(two\)。注意,由于我们的贪心策略,我们优先考虑 \(four\)。所以两组相等的二元组就会被我们合并成为一个四元组。
最后只要满足 \(four>0\) 且 \(two>1\),或者 \(four>1\),即拼出两个正方形,我们就输出 YES
。否则 NO
。
//Don't act like a loser.
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int maxn=1e5+10;
int n,cnt[maxn],m,four,two;
char ch;
signed main() {
n=read();
for(int i=1;i<=n;i++) {
int x=read();
cnt[x]++;
if(cnt[x]%4==0) {//先4后2
four++;
two--;
}
if(cnt[x]%4==2) {
two++;
}
}
m=read();
for(int i=1;i<=m;i++) {
cin>>ch;
int x=read();
if(ch=='+') {
cnt[x]++;
if(cnt[x]%4==0) {//先4后2
four++;
two--;
}
if(cnt[x]%4==2) {
two++;
}
}
else {
if(cnt[x]%4==0) {//先4后2
four--;
two++;
}
if(cnt[x]%4==2) {
two--;
}
cnt[x]--;
}
if(four>0&&two>1) {
printf("Yes\n");
}
else if(four>1) {
printf("YES\n");
}
else {
printf("NO\n");
}
}
return 0;
}
C. Pinkie Pie Eats Patty-cakes
此题的数学方法不再赘述,我们来关注一下冷门的二分答案(至少我没看到几个二分答案做的)。
确定做法二分答案,我们的重点就放在了 check
函数上。首先先讲讲我在考试的时候怎么想的(这个是错误想法)。
我们确定了一个答案 \(x\),判断是否可行,我们一个一个放数字,从出现次数最多的开始放,每隔 \(x\) 个位置就放一个,如果已经到头了,却还有剩余,就代表答案不存在,返回 \(0\)。但这是错的。我们举个例子,比如对于:
10
4 4 4 4 3 3 2 2 1 1
按照我们的做法,若 \(x=2\),放完了 \(4,3,2\) 后,是这样的一个序列:\(4\ 3\ 2\ 4\ 3\ 2\ 4\ 0\ 0\ 4\)。其中 \(0\) 代表还未放置的数。明显,我们会认为它是不可行的。其实我们可以实现,比如这个序列:\(4\ 3\ 2\ 4\ 1\ 2\ 4\ 1\ 3\ 4\)。这个check
直接暴毙。
我们来换一个贪心的思路。我们考虑到第 \(i\) 个位置,若在 \([i-x-1,i]\) 内 \(k\) 这个数还没有出现过,那么 \(k\) 就成为了 \(i\) 这个位置上的候选人。假设我们有 \(d\) 个候选人 \(k_1,k_2...k_d\)。我们就肯定先选择剩下还需在序列中出现次数最多的那个数,填在 \(i\) 这个位置上。因为填在这一位上,肯定比在 \(i+q\) 的位置上优。如果在考虑某个位置时,没有可以选择的数,即 \(d=0\) 时,肯定是无解的,返回 \(0\)。在计算时用一个堆维护即可。时间复杂度为 \(O(n\log^2 n)\)。可以卡过此题。
//Don't act like a loser.
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int maxn=1e5+10;
int n,a[maxn],cnt[maxn];
int b[maxn];
bool check(int x) {
fill(b+1,b+n+1,0);
priority_queue<pair<int,int> > q;
for(int i=1;i<=n;i++) {
if(cnt[i])
q.push(make_pair(cnt[i],i));
}
for(int i=1;i<=n;i++) {
if(i>x+1) {
if(cnt[b[i-x-1]])
q.push(make_pair(cnt[b[i-x-1]],b[i-x-1]));//把重新“合法”的这个数加回堆中。
}
if(q.empty()) {
return 0;//此时无解
}
pair<int,int> pr=q.top();
q.pop();
cnt[pr.second]--;//剩余出现次数减1
b[i]=pr.second;
}
return 1;
}
void clear() {
fill(cnt+1,cnt+n+1,0);
for(int i=1;i<=n;i++) {
cnt[a[i]]++;
}
}
signed main() {
int t=read();
while(t--) {
n=read();
for(int i=1;i<=n;i++) {
a[i]=read();
cnt[a[i]]++;
}
int l=0,r=n-2;
while(l+1<r) {
int mid=(l+r)>>1;
clear();//恢复cnt数组
if(check(mid)) {
l=mid;
}
else {
r=mid;
}
}
clear();//恢复cnt数组
if(check(r)) {
printf("%d\n",r);
}
else {
printf("%d\n",l);
}
fill(cnt+1,cnt+n+1,0);
}
return 0;
}
D. Rarity and New Dress
首先我们可以转化一下问题,我们先令一个满足衣服图案的区域的几何中心为这个衣服的中心,我们再令以 \((x,y)\) 为中心的最大的衣服的半径为 \(r_{x,y}\)。那么我们的答案就为:
对于半径我们结合图再来规定一下。对于下图(来自题目):
比如对于第一张图(左一),我们的半径为 \(1\);中间的那幅,半径为 \(2\);最右边的半径为 \(0\)。
我们来具体分析如何求解。
我们如果把这个图形分成左右两半,我们会发现都是一个金字塔的形状,且从上往下宽度以 \(2\) 为公差递增。这就是我们解决问题的切入点,分别取统计两边的金字塔大小,两边取 \(\min\),即为半径大小。但是怎么求金字塔大小?以左边为例我们先来求以 \((x,y)\) 为中心的上下方向的线段的最大长度,记为 \(len_{x,y}\)。然后我们再令 \((x,y)\) 左边的最大金字塔的长度为 \(left_{x,y}\)。则我们有 \(left_{x,y}=\min(\lfloor\frac{len_{x,y}}2\rfloor,left_{x,y-1}+1)\)。右边同理。这个问题就解决了。
顺便提一句:记得用 getchar()
,否则会被卡常。博主亲测过得哟,亲。
//Don't act like a loser.
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int maxn=2000+10;
int n,m,u[maxn][maxn],l[maxn][maxn],r[maxn][maxn],d[maxn][maxn],len[maxn][maxn];
char c[maxn][maxn];
signed main() {
n=read();m=read();
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
c[i][j]=getchar();
}
c[0][0]=getchar();
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
if(c[i][j]==c[i-1][j]) {
u[i][j]=u[i-1][j]+1;
}
}
}
for(int i=n;i>0;i--) {
for(int j=1;j<=m;j++) {
if(c[i][j]==c[i+1][j]) {
d[i][j]=d[i+1][j]+1;
}
len[i][j]=1+min(d[i][j],u[i][j])*2;
}
}
for(int i=1;i<=n;i++) {
for(int j=0;j<=m;j++) {
if(c[i][j]==c[i][j-1]) {
l[i][j]=min(len[i][j]/2,l[i][j-1]+1);
}
}
}
int ans=0;
for(int i=1;i<=n;i++) {
for(int j=m;j>0;j--) {
if(c[i][j]==c[i][j+1]) {
r[i][j]=min(len[i][j]/2,r[i][j+1]+1);
}
ans+=min(l[i][j],r[i][j])+1;
}
}
cout<<ans<<endl;
return 0;
}
Keep updating qwq
来源:oschina
链接:https://my.oschina.net/u/4357969/blog/4482233