Codeforces Round #580(Div.2) A~C解説

Codeforces Round #580 (Div.2)のA~Cまでの解説です。
コンテスト中はA~Cの3完でした(Div.1共催の回であったためD問題以降の難易度が高く、結果的にCの早解きができるかが鍵のコンテストでしたが、Cで手間取りレートを少し落としてしまいました…)。
問題のアドレスは以下になります↓
https://codeforces.com/contest/1206
公式の解説(Editorial)は以下になります↓
https://codeforces.com/blog/entry/69158

A問題:

制約が小さいため、全探索することを考えます。
すべてのi(1<=i<=n),j(1<=j<=m)について、ai+bjがAにもBにも含まれないようなi,jを求め、ai,bjを出力すればよいです。
ai+bjがA,Bに含まれるかどうかの判定は、A,Bの要素をset型などで持っておくことでO(logN+logM)で行うことができます。
以上のことから、この問題をO(NM(logN+logM))で解くことができました(A問題にしては易しめの印象でした)。

B問題:

明らかに、元の値が負であれば-1にし、元の値が正であれば+1にするのが最善です。
したがって、まずこの操作のコストを求めます。
次に、元の値に0があるかどうかで場合分けし、操作終了後のすべての要素の積が1になるように調整するためのコストを求めます。
元の値に0がないとき、元の値が負であるような数が偶数個あれば、調整を行わずともすべての要素の積は1となります。
一方、元の値が負であるような数が奇数個あれば、1回だけ-1または1の符号を反転させる必要があり、このときコストは2増加します。
元の値に0があるとき、すべての0を-1または1にする必要があるためコストは0の個数だけ増加します。
さらに、元の値が負であるような数が偶数個であれば、すべての0を1にしてしまえばよく、元の値が負であるような数が奇数個であれば、0を1つだけ-1にし、それ以外の0を1にしてしまうことですべての要素の積が1になります。
したがって、元の値に0があるとき、元の値が負であるような数の個数に関わらずコストは0の個数だけ増加します。
以上の場合分けを実装すればよく、この操作の計算量はO(N)となります。

サンプルコード(Python):
https://codeforces.com/contest/1206/submission/59010270

n=int(input())
arr=list(map(int,input().split()))
move=0
cnt=0
pcnt=0
ncnt=0
for i in range(n): #各要素について、-1または1に変更するコストを求める
  if arr[i]>0: #要素が正のとき
    move+=(arr[i]-1)
    arr[i]=1
    pcnt+=1
  elif arr[i]<0: #要素が負のとき
    move+=(-1-arr[i])
    arr[i]=-1
    ncnt+=1
  else: #要素が0のとき
    cnt+=1
if pcnt+ncnt==n: #配列に0がないとき
  if ncnt%2==0: #負数の数が偶数個であれば調整は必要ない
    print(move)
  else: #負数の数が奇数個であれば-1を1つ増やす必要があり、コストが2増加する
    print(move+2)
else: #配列に0がないとき
  if ncnt%2==0: #負数の数によらず、0の個数だけコストが増加する
    print(move+cnt)
  else:
    print(move+cnt)

C問題:

まず、どのようなnについて構成が可能かを考えます。
n個の連続する和を2n個作るとき、1から2nまでの値はそれぞれn回現れるので、それらの総和はn*2n*(2n+1)/2=n^2(2n+1)となります。
この値は、nが偶数のとき偶数となり、nが奇数のとき奇数となります。
総和が偶数のとき、これを2n個の和に分割すると、すべての和が等しくなるか、もしくはそれぞれの和の差が2以上になります。
それぞれの和の差が2以上になるとき、明らかに条件に反し、またすべての和が等しくなるとき、同じ値を使う必要がありますが、これも条件に反します。
したがって、nが偶数のときには解を構成することはできません。
逆に、nが奇数のときは必ず解を構成することができます。
以下、このことを示します。
まず、前提として、任意のi(1<=i<=2n)についてai+a(i+1)+…+a(i+n-1)とa(i+1)+…+a(i+n-1)+a(i+n)を考えると、条件より|(ai+a(i+1)+…+a(i+n-1))-(a(i+1)+…+a(i+n-1)+a(i+n))|<=1⇔|ai-a(i+n)|<=1を満たすことがいえます(ただしakについて、k>2nとなるときは、a(k-2n)を指すものとします)。
nが奇数のとき、総和が奇数となり、これを条件を満たすように2n個の和に分割すると、n個の値がk、残りn個の和がk+1となるようなkを取ることができます。
簡単のため、a1=1、a(n+1)=2と仮定します。
このとき、a1+a2+…+anがkでなければ、a2+a3+…+an+a(n+1)がk+1より大きくなってしまい、条件に反します。
したがって、a1+a2+…+anはk、a2+…+an+a(n+1)はk+1となります。
これを続けていくと、a3+…+an+a(n+1)+a(n+2)はkとなり、a4+…+an+a(n+1)+a(n+2)+a(n+3)はk+1となり、…と続いていきます。
これから、a2=a(n+2)+1,a3=a(n+3)-1,a4=a(n+4)+1,…,a(n)=a(2n)+1(nが偶数)、a(n)=a(2n)-1(nが奇数)と表すことができ、またこの条件を満たすようにa2,…,an、a(n+2),…a(2n)を定めると、上記の関係よりn個の和がk、残りn個の和がk+1となることが分かります。
したがって、この条件を満たすように適当にa2,…,an、a(n+2),…a(2n)を定めることで、この問題を解くことができます。
このようなa2,…,an、a(n+2),…a(2n)の決め方としては、2nから3までの数を以下のように互い違いに並べていき、各行をそれぞれa2,…,an、a(n+2),…a(2n)とすることで決めることができます。
例えば2n=10から3までの数を並べるときには、
10 7 6 3
9 8 5 4
というふうにならべ、10,7,6,3をa2,…,a5、9,8,5,4をa8,…a12と定めればよいです。
以上の操作はO(N)で行うことができるため、この問題を解くことができました。

サンプルコード(Python):
https://codeforces.com/contest/1206/submission/59058325

n=int(input())
if n%2==0: #nが偶数のときは構成は不可能
   print('NO')
else: #nが奇数のときは上記の方法で構成が可能
   print('YES')
   arr1=[]
   arr2=[]
   for i in range(2*n,2,-1):
       tmp=i%4
       if tmp==2 or tmp==3: #4で割ったあまりが2または3のときは1行目、それ以外のときは2行目に書かれる
           arr1.append(i)
       else:
           arr2.append(i)
   ans=[1]+arr1+[2]+arr2 #答えはa1=1,a2,…,an=arr1,a(n+1)=2,a(n+2),…,a(2n)=arr2となる
   print(*ans)

この記事が気に入ったらサポートをしてみませんか?