Chain Chips (Codeforces 1814E)
Tags: DP, matrices

You are given an undirected graph consisting of $$$n$$$ vertices and $$$n-1$$$ edges. The $$$i$$$-th edge has weight $$$a_i$$$; it connects the vertices $$$i$$$ and $$$i+1$$$.Initially, each vertex contains a chip. Each chip has an integer written on it; the integer written on the chip in the $$$i$$$-th vertex is $$$i$$$.In one operation, you can choose a chip (if there are multiple chips in a single vertex, you may choose any one of them) and move it along one of the edges of the graph. The cost of this operation is equal to the weight of the edge.The cost of the graph is the minimum cost of a sequence of such operations that meets the following condition:  after all operations are performed, each vertex contains exactly one chip, and the integer on each chip is not equal to the index of the vertex where that chip is located. You are given $$$q$$$ queries of the form:  $$$k$$$ $$$x$$$ — change the weight of the $$$k$$$-th edge (the one which connects the vertices $$$k$$$ and $$$k+1$$$) to $$$x$$$. After each query, print the cost of the graph. Note that you don't actually move any chips; when you compute the cost, the chips are on their initial positions.

Input: The first line contains one integer $$$n$$$ ($$$2 \le n \le 2 \cdot 10^5$$$).The second line contains $$$n-1$$$ integers $$$a_1, a_2, \dots, a_{n-1}$$$ ($$$1 \le a_i \le 10^9$$$).The third line contains one integer $$$q$$$ ($$$1 \le q \le 2 \cdot 10^5$$$).Then $$$q$$$ lines follow. The $$$i$$$-th of them contains two integers $$$k$$$ and $$$x$$$ ($$$1 \le k \le n-1$$$; $$$1 \le x \le 10^9$$$) for the $$$i$$$-th query.

Output: For each query, print one integer — the cost of the graph after the query is performed.


Sample Inputs:
10
12 6 12 15 20 8 17 12 15
8
4 10
7 3
6 14
9 9
2 10
3 5
4 11
7 11

Sample Outputs:
126
108
120
108
112
98
98
114

Solution (Python):
import sys, os, io
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline

def f(u, l, r):
    return 4 * u + 2 * l + r

def g(u):
    (v, w) = (2 * u, 2 * u + 1)
    for l in range(2):
        for r in range(2):
            dp0 = -inf
            for l0 in range(2):
                for r0 in range(2):
                    if not l0 & r0:
                        dp0 = max(dp0, dp[f(v, l, l0)] + dp[f(w, r0, r)])
            dp[f(u, l, r)] = dp0
    return

def update(u, f0):
    u += l1
    dp[f(u, 0, 0)] = 0
    dp[f(u, 1, 1)] = a[u - l1]
    dp[f(u, 0, 1)] = -inf
    dp[f(u, 1, 0)] = -inf
    if not f0:
        return
    u //= 2
    while u:
        g(u)
        u //= 2
    return
n = int(input())
a = [0] + list(map(int, input().split()))
l1 = pow(2, n.bit_length())
l2 = 2 * l1
inf = pow(10, 9) + 1
dp = [0] * (4 * l2)
for i in range(2, n - 1):
    update(i, 0)
for i in range(l1 - 1, 0, -1):
    g(i)
s = sum(a)
q = int(input())
ans = []
for _ in range(q):
    (k, x) = map(int, input().split())
    s += x - a[k]
    a[k] = x
    if 2 <= k < n - 1:
        update(k, 1)
    ans0 = 2 * (s - max(dp[4], dp[5], dp[6], dp[7]))
    ans.append(ans0)
sys.stdout.write('\n'.join(map(str, ans)))

Solution (Python):
import sys, os, io
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline

def f(u, l, r):
    return 4 * u + 2 * l + r

def g(u):
    (v, w) = (2 * u, 2 * u + 1)
    for l in range(2):
        for r in range(2):
            dp0 = -inf
            for l0 in range(2):
                for r0 in range(2):
                    if not l0 & r0:
                        dp0 = max(dp0, dp[f(v, l, l0)] + dp[f(w, r0, r)])
            dp[f(u, l, r)] = dp0
    return

def update(u, f0):
    u += l1
    dp[f(u, 0, 0)] = 0
    dp[f(u, 1, 1)] = a[u - l1]
    dp[f(u, 0, 1)] = -inf
    dp[f(u, 1, 0)] = -inf
    if not f0:
        return
    u //= 2
    while u:
        g(u)
        u //= 2
    return
n = int(input())
a = [0] + list(map(int, input().split()))
l1 = pow(2, n.bit_length())
l2 = 2 * l1
inf = pow(10, 9) + 1
dp = [0] * (4 * l2)
for i in range(2, n - 1):
    update(i, 0)
for i in range(l1 - 1, 0, -1):
    g(i)
s = sum(a)
q = int(input())
ans = []
for _ in range(q):
    (k, x) = map(int, input().split())
    s += x - a[k]
    a[k] = x
    if 2 <= k < n - 1:
        update(k, 1)
    ans0 = 2 * (s - max(dp[4], dp[5], dp[6], dp[7]))
    ans.append(ans0)
sys.stdout.write('\n'.join(map(str, ans)))

Solution (Python):
import sys
import os
from io import BytesIO, IOBase
from itertools import chain
BUFSIZE = 8192

class FastIO(IOBase):
    newlines = 0

    def __init__(self, file):
        self._fd = file.fileno()
        self.buffer = BytesIO()
        self.writable = 'x' in file.mode or 'r' not in file.mode
        self.write = self.buffer.write if self.writable else None

    def read(self):
        while True:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            if not b:
                break
            ptr = self.buffer.tell()
            (self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
        self.newlines = 0
        return self.buffer.read()

    def readline(self):
        while self.newlines == 0:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            self.newlines = b.count(b'\n') + (not b)
            ptr = self.buffer.tell()
            (self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
        self.newlines -= 1
        return self.buffer.readline()

    def flush(self):
        if self.writable:
            os.write(self._fd, self.buffer.getvalue())
            (self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        self.write = lambda s: self.buffer.write(s.encode('ascii'))
        self.read = lambda : self.buffer.read().decode('ascii')
        self.readline = lambda : self.buffer.readline().decode('ascii')
if sys.version_info[0] < 3:
    sys.stdin = BytesIO(os.read(0, os.fstat(0).st_size))
else:
    (sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
file = sys.stdin
if os.environ.get('USER') == 'loic':
    file = open('data.in')
line = lambda : file.readline().split()
ui = lambda : int(line()[0])
mi = lambda : map(int, line())
li = lambda : list(mi())
lsi = lambda : list(map(int, line()[0]))

class SegmentTree:

    def __init__(self, sz):
        self.sz = 1 << (sz - 1).bit_length()
        self.node = [self._neutral() for _ in range(2 * self.sz)]

    def load(self, data):
        for i in range(len(data)):
            self._initialize(i, data[i])
        r = self.sz - 1
        size = 2
        for i in range(self.sz - 1, 0, -1):
            l = r - size + 1
            self._merge(self.node[i], self.node[i << 1], self.node[i << 1 | 1], l, r)
            if l == 0:
                r = self.sz - 1
                size <<= 1
            else:
                r -= size

    def update(self, pos, new_val):
        self._initialize(pos, new_val)
        l = pos
        r = pos
        size = 1
        i = self.sz + pos
        while i > 1:
            if i & 1:
                l -= size
            else:
                r += size
            i >>= 1
            self._merge(self.node[i], self.node[i << 1], self.node[i << 1 | 1], l, r)
            size <<= 1

    def query(self, l, r):
        tl = self.sz + l
        tr = self.sz + r + 1
        ql = []
        qr = []
        size = 1
        while tl < tr:
            if tl & 1:
                ql.append((tl, size))
                tl += 1
            if tr & 1:
                tr -= 1
                qr.append((tr, size))
            tl >>= 1
            tr >>= 1
            size <<= 1
        cur_r = l
        left = self._neutral()
        for (i, sz) in chain(ql, reversed(qr)):
            cur_r += sz
            res = self._neutral()
            self._merge(res, left, self.node[i], l, cur_r - 1)
            left = res
        return self._res(res)

    def _neutral(self):
        return [0] * 5

    def _initialize(self, pos, val):
        node = self.node[self.sz + pos]
        node[4] = val
        node[0] = 0
        node[1] = Z
        node[2] = Z
        node[3] = val
        if pos == 0 or pos == N - 2:
            node[0] = Z

    def _merge(self, node, left, right, l, r):
        node[0] = min(left[0] + right[2], left[1] + right[2], left[1] + right[0])
        node[1] = min(left[0] + right[3], left[1] + right[3], left[1] + right[1])
        node[2] = min(left[2] + right[2], left[3] + right[0], left[3] + right[2])
        node[3] = min(left[2] + right[3], left[3] + right[1], left[3] + right[3])
        node[4] = min(node[:4])

    def _res(self, node):
        return node[4]

    def __repr__(self):
        return repr(self.node)

def solve():
    res = []
    st = SegmentTree(N - 1)
    st.load(A)
    for _ in range(Q):
        (K, X) = mi()
        K -= 1
        st.update(K, X)
        res.append(2 * st.node[1][4])
    return '\n'.join((str(v) for v in res))
Z = 10 ** 18
for test in range(1, 1 + 1):
    N = ui()
    A = li()
    Q = ui()
    print(solve())
file.close()