AtCoder茶色解く2

今回解くのはこちら
D - Banned K

Nの数は2*10^5が上限
Aの数は1~Nまで

まずは簡単にするために、すべてのAを数え上げて記録する

N = int(input())
A = list(map(int, input().split()))
di = {}

for a in A:
  if a not in di.keys(): # diに何もなかった場合
    #print("add")
    di[a] = 1
  else:
    #print("sum")
    di[a] += 1
#print(di)


さらに、得られたまとめデータをもとに各Aに対してfor文を回す

# 組み合わせ計算
sum = 0
for k in A:
  for a, b in di.items():
    if a == k: b -= 1
    #print(a,b)
    sum += int((b*(b-1))/2*1)
  print(sum)
  sum = 0

一部がTLE出ている
よって、最適化を行っていく

まずは、計算結果をメモするように変更した

# 組み合わせ計算
sum = 0
ll = {}
for k in A:
  if k not in ll.keys():
    for a, b in di.items():
      if a == k: b -= 1
      #print(a,b)
      sum += int((b*(b-1))/2*1)
    print(sum)
    ll[k] = sum
  else:
    print(ll[k])
  sum = 0

これで一部高速化でき、ACの数は増えたが、以前TLEが存在する状況であった。

ここで、以下記事を参考に考え方を変えた
AtCoder ABC 159 D - Banned K (400 点) - けんちょんの競プロ精進記録

愚直に毎回の数列について計算したのでは O(N2) の計算量を要してしまう。今回のような、各 k について、それを除いたものについて考えるタイプの問題では

まず全体の数列についての答えを求めておいて
各要素を取り除くことによる「影響」を計算し、
その影響分を引く

先に組み合わせ数の計算値を求めておき、そこから各パターンを追加で計算する。
差分更新というやり方らしい

これで書き換えたものが以下

N = int(input())
A = list(map(int, input().split()))
di = {}

for a in A:
  if a not in di.keys(): # diに何もなかった場合
    #print("add")
    di[a] = 1
  else:
    #print("sum")
    di[a] += 1

# 全体量計算
sum = 0
for a, b in di.items():
  #print(a,b)
  sum += int((b*(b-1))/2*1)
  
# 組み合わせ計算
for k in A:
  n = di[k]
  bef = n*(n-1)/2
  aft = (n-1)*(n-2)/2
  print(int(sum-bef+aft))

無事ACが取れました。