1. 程式人生 > 其它 >六省聯考2017 題解

六省聯考2017 題解

D2T1 啥毒瘤題啊,本篇題解不涉及。不過有一說一 D2T3 還是非常人性化的。

P3745 [六省聯考 2017] 期末考試

\(m\) 門考試,第 \(i\) 門考試原計劃在第 \(b_i\) 天出成績。有 \(n\) 個學生在等待成績,第 \(i\) 個學生希望在第 \(t_i\) 天前得知所有的成績。設最晚出成績的那門考試在第 \(T\) 天,則有以下三種情況會產生不愉快度。

  • 每一名學生會產生 \(C\max(T-t_i,0)\) 的不愉快度。
  • 將第 \(x\) 門考試推遲一天出成績並將第 \(y\) 門考試提前一天出成績,產生 \(A\) 的不愉快度。
  • 將第 \(x\) 門考試提前一天出成績,產生 \(B\)
    的不愉快度。

求可能的不愉快度最小值。(\(1\le n,m,t_i,b_i\le 10^5,0\le A,B,C\le 10^5\))

注意到調整後的 \(T\) 一定不不調整的 \(T\) 更小,且注意到 \(T\le 10^5\),所以考慮列舉調整後的 \(T\),並計算調整到 \(T\) 的代價,計算出學生的不愉快度後取個 \(\min\) 即可。而想要調整到 \(T\),會有一些科目 需要提前,還有一些科目 可以推遲。現在我們需要找到一個貪心策略來調整。

注意到如果 \(B<A\),則直接用費用更低且沒有限制的操作即可,如果總共需要提前 \(x\) 天,則產生的代價顯然為:

\[Bx \]

而如果 \(A<B\)

,則我們需要儘可能利用可以推遲的科目,即總共如果需要提前 \(x\) 天,可以推遲 \(y\) 天,則產生的代價為:

\[A\min(x,y)+B\max(0,x-y) \]

注意到只要知道 \(x,y\),根據貪心策略我們都可以在 \(\mathcal{O}(1)\) 的時間計算出需要的調整時間。直接算看起來好像不太可行,所以我們考慮每次 \(T\) 減小時會對 \(x,y\) 分別造成什麼影響。首先,如果對每門考試的 \(b_i\) 排序,我們可以用一個指標在均攤 \(\mathcal{O}(1)\) 的時間下求出在 \(T\) 前和後的考試科目,分別設為 \(las,nxt\),和 \(b_i=T\)

的科目個數,設為 \(cnt\),則 \(x,y\) 分別會變為:

\[x+nxt-cnt,y-las-cnt \]

這樣我們就能在均攤 \(\mathcal{O}(1)\) 的時間複雜度內維護 \(x,y\) 了。

而知道代價之後,我們還要知道學生產生不滿意度 \(now\)。注意到把 \(t_i\) 排序後依然是滿足單調性的,所以還考慮用一個指標維護有多少學生會產生不滿意度。對於之前產生現在不產生的學生,設為 \(l\),則 \(now\) 會變為:

\[now-lC \]

而對於其他的,設為 \(p\)\(now\) 類似地,會變為:

\[now-pC \]

這倆不一樣的是,\(lC\) 的貢獻之後不會再產生了。而一開始的 \(now\) 可以 \(\mathcal{O}(n)\) 列舉求出,並找到指標的初始位置。至此,我們就能在 \(\mathcal{O}(1)\) 的時間複雜度內維護好所有需要的資訊,總時間複雜度 \(\mathcal{O}(n+m+\max t_i)\)

#include <cstdio>
#include <algorithm>
const int N = 1e5 + 10; typedef long long ll;
int a[N], b[N], A, B, C, n, m;
int main()
{
    scanf("%d%d%d%d%d", &A, &B, &C, &n, &m); ll ans = 1e18;
    for (int i = 1; i <= n; ++i) scanf("%d", a + i);
    for (int i = 1; i <= m; ++i) scanf("%d", b + i);
    std::sort(a + 1, a + n + 1); std::sort(b + 1, b + m + 1);
    ll res = 0, ned = 0, now = 0, add; int posa = n, posb = m;
    while (posb >= 1 && b[posb] == b[m]) --posb;
    for (int i = 1; i < m; ++i) res += (b[m] - b[i]);
    for (int i = 1; i <= n; ++i) 
    {
        if (a[i] >= b[m]) { posa = i - 1; break; }
        now += (ll)C * (b[m] - a[i]); 
    }
    ans = std::min(ans, now);
    for (int i = b[m] - 1, cnt; i >= 1; --i)
    {
        cnt = 0;
        while (posb >= 1 && b[posb] == i) --posb, ++cnt;
        res -= posb + cnt; ned += m - posb - cnt;
        if (A < B && res > 0) add = std::min(res, ned) * A + std::max(0ll, ned - res) * B;
        else add = ned * B;
        while (posa >= 1 && a[posa] == i) now -= C, --posa; 
        now -= (ll)posa * C; ans = std::min(ans, now + add);
    }
    printf("%lld\n", ans); return 0;
}

P3747 [六省聯考 2017] 相逢是問候

維護一個長為 \(n\) 的陣列,給出常數 \(c,p\),有以下兩種操作:

  • 將第 \(l\) 到第 \(r\) 個數中的每個 \(a_i\) 替換為 \(c^{a_i}\)
  • 求出 \(\sum\limits_{i=l}^ra_i\bmod{p}\)

(\(1\le n,m\le 5\times10^4,1\le c,a_i<p\le 10^8\))

發現題目與指數有關,考慮拓展尤拉定理:

\[a^b=\begin{cases}a^{b\bmod{\varphi(m)}}&\gcd(a,m)=1\\a^b&b<\varphi(m)\\a^{b\bmod{\varphi(m)}+\varphi(m)}&b\ge \varphi(m)\end{cases}\bmod{m} \]

注意到題目不保證 \(\gcd(p,c)=1\),所以考慮後兩個等式。如果您做過 P4139 上帝與集合的正確用法 這道題的話,就會知道,像本題給出的指數疊疊高的形式在模意義下是有盡頭的,即超過一定層數後的指數就不會產生貢獻了。可以通過考慮把指數看為遞迴形式,而 \(\varphi(\varphi(\varphi\cdots(m)))\) 的形式,會在一定次數後變為 \(1\),超過這個次數後的指數就變為 \(0\) 了。而在本題中,一旦 \(a_i\) 變為 \(0\),之後再操作值就不會改變了,一直為:

\[c^{c^{c^{\cdots}}} \]

而又注意到,\(\varphi(\varphi(m))\) 至少會把 \(m\) 縮小 \(2\) 倍,考慮 \(\varphi(m)\) 的一種求法:

\[\varphi(m)=\prod_{i=1}^k\varphi(p_i^{c_i})=\prod_{i=1}^k(p_i^{c_i}-p_i^{c_i-1})=\prod_{i=1}^kp_i^{c_i-1}(p_i-1)=m\prod_{i=1}^k\dfrac{p_i-1}{p_i} \]

注意到如果 \(m\) 為奇數則至少會有一個積的質因子變為偶數,這樣 \(\varphi(m)\) 就會變為偶數。而如果 \(m\) 為偶數,則 \(2\) 會變為 \(1\),所以不管 \(m\) 的值是多少,\(\varphi(\varphi(m))\) 至少把 \(m\) 縮小 \(2\) 倍。這樣的話,能發現需要考慮的層數非常少,僅有 \(\mathcal{O}(\log p)\) 層。

發現了什麼?雖然這個區間操作並不好直接維護,但有影響的操作次數很少!這不免讓我們聯想起線段樹維護區間開方這種利用勢能分析保證複雜度維護方法。所以類比一下,我們考慮用線段樹維護這個操作,考慮在每個結點維護兩個值。

  • \(minx\) 表示當前區間的最高層數。
  • \(sum\) 表示當前區間的區間和。

而顯然如果一個區間的 \(minx\) 都大於最高層數,我們就不用再對這個區間進行操作了,否則就遞迴下去暴力單點操作。可以用我不會的勢能分析證明這個複雜度是正確的 \(\mathcal{O}(m\log p+n\log n)\)

現在我們要解決的問題一下子就變成了如何對一個單點進行修改。注意到我們記錄了這個區間疊高高疊了多少層,所以可以類比 P4139 上帝與集合的正確用法 的方法,用遞迴的形式計算,這就要求我們知道 \(\varphi(p),\varphi(\varphi(p)),\cdots\) 的值,但因為 \(p\)\(10^8\) 級別,直接線性篩顯然是不行的,但我們可以用剛剛提到的方法提前暴力算出這些值,時間複雜度 \(\mathcal{O}(\sqrt{p}\log p)\),可以接受。而遞迴的層數是 \(\log p\) 層,每層需要一次 \(\log p\) 的快速冪,這樣一次計算就是 \(\mathcal{O}(\log^2p)\) 的時間複雜度,總的複雜度即為 \(\mathcal{O}(\sqrt{p}\log p+m\log^3p+n\log n)\),非常難以通過。

回顧剛剛的過程,幾乎所有的過程都難以再優化了,除了快速冪的那隻 \(\log\),考慮預處理冪和冪的大小是否超過模數,用光速冪做到 \(\mathcal{O}(1)\)。但注意要對所有可能出現的 \(\varphi\) 預處理,所以時間複雜度為 \(\mathcal{O}(\sqrt{m}\log p)\),這樣總時間複雜度就被優化至 \(\mathcal{O}((\sqrt{p}+\sqrt{m})\log p+m\log^2p+n\log n)\),足以通過。

#include <cstdio>
#include <algorithm>
const int N = 5e4 + 10, D = 100, B = 1e4; typedef long long ll;
int pw[2][D][B + 10], ov[2][D][B + 10], phi[D], dep, n, m, p, c, over, a[N];
inline int Phi(int n)
{
	int res = n;
	for (int i = 2; i * i <= n; ++i)
	{
		if (n % i == 0) res = res / i * (i - 1);
		while (n % i == 0) n /= i; 
	}
	if (n > 1) res = res / n * (n - 1); return res;
}
inline void pre()
{
	phi[0] = p; ll t;
	while (phi[dep] > 1) ++dep, phi[dep] = Phi(phi[dep - 1]);
	phi[++dep] = 1;
	for (int i = 0; i <= dep; ++i)
	{
		pw[0][i][0] = 1;
		for (int j = 1; j <= B; ++j)
		{
			t = (ll)pw[0][i][j - 1] * c; if (t >= phi[i]) ov[0][i][j] = 1;
			pw[0][i][j] = t % phi[i]; ov[0][i][j] |= ov[0][i][j - 1];
		}
		pw[1][i][0] = 1;
		for (int j = 1; j <= B; ++j)
		{
			t = (ll)pw[1][i][j - 1] * pw[0][i][B]; if (t >= phi[i]) ov[1][i][j] = 1;
			pw[1][i][j] = t % phi[i]; ov[1][i][j] |= ov[1][i][j - 1];
		}
	}
} 
inline int gsm(int d, int b)
{
	int v1 = b / B, v2 = b % B; ll t;
	over |= ov[0][d][v2] | ov[1][d][v1];
	t = (ll)pw[0][d][v2] * pw[1][d][v1]; if (t >= phi[d]) over = 1;
	return t % phi[d];
}
int calc(int p, int cnt, int d)
{
	over = 0;
	if (d == cnt)
	{
		if (a[p] >= phi[d]) over = 1;
		return a[p] % phi[d];
	}
	int x = calc(p, cnt, d + 1);
	if (over) x += phi[d + 1], over = 0;
	return gsm(d, x);
}
struct SegTree
{
	#define ls(k) (k << 1)
	#define rs(k) (k << 1 | 1)
	struct node{ int l, r, cnt, sum; }h[N << 2];
	void pushup(int k) 
	{ 
		h[k].cnt = std::min(h[ls(k)].cnt, h[rs(k)].cnt); 
		h[k].sum = (h[ls(k)].sum + h[rs(k)].sum) % p;
	}
	void build(int k, int l, int r)
	{
		h[k].l = l; h[k].r = r;
		if (l == r) return h[k].sum = a[l], void();
		int mid = (l + r) >> 1; build(ls(k), l, mid); build(rs(k), mid + 1, r);
		pushup(k);
	}
	void change(int k, int x, int y)
	{
		if (h[k].cnt >= dep) return ;
		if (h[k].l == h[k].r) return ++h[k].cnt, h[k].sum = calc(h[k].l, h[k].cnt, 0), void();
		int mid = (h[k].l + h[k].r) >> 1; 
		if (x <= mid) change(ls(k), x, y); if (mid < y) change(rs(k), x, y);
		pushup(k);
	}
	int query(int k, int x, int y)
	{
		if (x <= h[k].l && h[k].r <= y) return h[k].sum;
		int mid = (h[k].l + h[k].r) >> 1, ret = 0;
		if (x <= mid) (ret += query(ls(k), x, y)) %= p;
		if (mid < y) (ret += query(rs(k), x, y)) %= p;
		return ret;
	}
}sgt;
int main()
{
	scanf("%d%d%d%d", &n, &m, &p, &c); pre();
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	sgt.build(1, 1, n);
	for (int i = 1, op, l, r; i <= m; ++i)
		scanf("%d%d%d", &op, &l, &r), op ? printf("%d\n", sgt.query(1, l, r)), void() : sgt.change(1, l, r);
	return 0;
}

P3746 [六省聯考 2017] 組合數問題

給出 \(n,p,k,r\),求下列式子的值:

\[\left(\sum_{i\ge0}\dbinom{nk}{ik+r}\right)\bmod{p} \]

(\(1\le n\le 10^9,0\le r<k\le 50,2\le p\le 2^{30}-1\))

首先如果 \(p\) 保證是質數,即一定存在逆元,可以用定義式算,用下式在 \(\mathcal{O}(nk)\) 的時間複雜度下進行遞推計算:

\[\dbinom{nk}{(i+1)k+r}=\dbinom{nk}{ik+r}\times\dfrac{(nk-ik-r)^{\underline{k}}}{(ik+k+r)^{\underline{k}}} \]

證明可以通過把組合數拆成階乘看出,期望得分 \(\tt 60pts\)

但當 \(n\) 比較大,或者 \(p\) 不保證質數時,上面的方法就無法採用了。因為逆元不一定存在,從數學這方面的推導幾乎就被封死了,所以我們可以從組合數本身進行思考。注意到組合數 \(\binom{n}{m}\) 的組合意義是從 \(n\) 個物品裡選 \(m\) 個的方案數,而套用到這道題上,我們會得到:

  • \(nk\) 個物品裡選 \(r,k+r,2k+r,\cdots\) 個物品的方案數之和。

而這些物品個數有很強的共性:均模 \(k\)\(r\)。而 \(k\) 又很小,這啟發我們把問題控制在 \(k\) 的剩餘系內。考慮設 \(f_{i,j}\) 表示考慮了 \(i\) 個物品,選出的個數模 \(k\)\(j\) 的方案數,則最終答案即為 \(f_{nk,r}\),列舉每個物品是否選取,可以得到以下轉移方程:

\[f_{i-1,j}+f_{i-1,(j-1)\bmod{k}}\rightarrow f_{i,j},f_{0,0}=1 \]

直接暴力轉移是 \(\mathcal{O}(nk^2)\),不如暴力。注意到這個形式是常係數齊次線性遞推的形式,可以考慮用矩乘優化:

\[\begin{bmatrix}f_{i,0}&f_{i,1}&\cdots&f_{i,k-1}\end{bmatrix}\begin{bmatrix}1&0&\cdots&0&1\\1&1&\cdots&0&0\\\vdots&\vdots&\ddots&\vdots&\vdots\\0&0&\cdots&1&1\end{bmatrix}=\begin{bmatrix}f_{i+1,0}&f_{i+1,1}&\cdots&f_{i+1,k-1}\end{bmatrix} \]

實現個矩陣快速冪就可以在 \(\mathcal{O}(k^3\log (nk))\) 的時間複雜度內求解了。

#include <cstdio>
const int N = 60; typedef long long ll; int n, p, k, r;
struct Matrix
{
    int a[N][N], n, m;
    int* operator[](int n) { return a[n]; }
    void init(int N, int M, int on = 0)
    {
        n = N; m = M;
        for (int i = 1; i <= n; ++i)
                for (int j = 1; j <= m; ++j) a[i][j] = on ? (i == j) : 0;
    }
    Matrix operator*(Matrix A)
    {
        Matrix ret; ret.init(n, A.m);
        for (int i = 1; i <= ret.n; ++i)
            for (int j = 1; j <= ret.m; ++j)
                for (int k = 1; k <= m; ++k) (ret[i][j] += (ll)a[i][k] * A[k][j] % p) %= p;
        return ret;
    }
}A, B, I;
Matrix ksm(Matrix A, ll b)
{
    Matrix ret = I;
    while (b)
    {
        if (b & 1) ret = ret * A;
        A = A * A; b >>= 1;
    }
    return ret;
}
int main()
{
    scanf("%d%d%d%d", &n, &p, &k, &r);
    A.init(1, k); B.init(k, k, 1); I.init(k, k, 1); A[1][1] = 1;
    for (int i = 2; i <= k; ++i) ++B[i][i - 1]; ++B[1][k];
    B = ksm(B, (ll)n * k); A = A * B; printf("%d\n", A[1][r + 1]); return 0;
}

P3748 [六省聯考 2017] 摧毀“樹狀圖”

\(\tt deleted\)

P3750 [六省聯考 2017] 分手是祝願

\(n\) 個燈,初始時有亮有暗,按下第 \(i\) 個開關會導致所有的 \(d\) 滿足 \(d|i\) 對應位置的燈改變亮暗。現在一個人隨機按開關,但噹噹前狀態下最優策略可以在 \(\le k\) 步將燈全部關掉,這個人就會按照最優策略操作。求這個人的期望操作次數乘 \(n!\) 的值,答案對 \(10^5+3\) 取模。(\(1\le n\le 10^5,0\le k\le n\))

我們先不管題目,來考慮一下對於給出狀態的燈,怎麼操作才是最優的。注意到一次按開關,能影響到的都是編號比它小的,而我們只要不走回頭路就能保證操作次數最小了,即我們按照編號從大到小考慮燈,如果亮就按開關,並計算影響,否則就不管。這樣計算的時間複雜度是 \(\mathcal{O}(n\sqrt{n})\),完全沒有問題。證明可以感性理解,我們只按必須按的開關。

現在我們知道了最大操作次數 \(m\),接下來就該算期望了。考慮 \(\rm dp\),設 \(f_i\) 表示最優策略是按 \(i\) 下開關時需要的期望操作次數。注意到開關造成的影響是唯一的,即不存在一個開關組可以替換某個開關,所以如果隨機按開關按錯了(即不在最優策略裡)之後還一定要按一次按回來,所以有轉移:

\[f_i=\dfrac{i}{n}f_{i-1}+\dfrac{n-i}{n}f_{i+1}+1 \]

好了,做個高消,取 \(f_m\) 這道題就做完了,時間複雜度 \(\mathcal{O}(n)\)

但非常遺憾我已經不會高消了,所以我們來再盯著這個式子看一會兒。發現這個式子需要高消的根本原因是有三項,無法遞推,那我們乾脆就把它變成一項!考慮差分陣列:

\[d_i=f_i-f_{i-1} \]

則原式可以化為:

\[f_i=\dfrac{i}{n}(f_i-d_i)+\dfrac{n-i}{n}(f_i+d_{i+1})+1 \]

簡單的代數變化後,你會發現,\(f_i\) 從兩邊消去了!然後我們得到了什麼呢?一個關於 \(d_i\) 的遞推式!

\[d_i=\dfrac{n-i}{i}d_{i+1}+\dfrac{n}{i} \]

這東西可以倒著推,所以現在我們的問題就是找到一個 \(d_i(i\ge m)\) 的值倒著推回來,再根據 \(f_k=k\),正著推到 \(f_m\) 即為答案。考慮最小操作次數為 \(n\) 的情況,這時候不管按哪個開關都在最優策略內,這樣關於 \(f\) 的等式就少了 \(f_{i+1}\) 這一項,即:

\[f_n=f_{n-1}+1 \]

所以我們就得到:

\[d_n=f_n-f_{n-1}=1 \]

而又因為 \(m\le n\),所以我們就找到了一個符合條件的值!直接遞推即可,總時間複雜度 \(\mathcal{O}(n\sqrt{n})\)。注意特判 \(k\ge m\) 的情況。

#include <cstdio>
const int N = 1e6 + 10, mod = 100003; int a[N], f[N], d[N];
inline int ksm(int a, int b)
{
    int ret = 1;
    while (b)
    {
        if (b & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod; b >>= 1;
    }
    return ret;
}
int main()
{
    int n, m = 0, k, fac = 1; scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), fac = 1ll * fac * i % mod;
    for (int i = n; i >= 1; --i)
    {
        if (!a[i]) continue;
        for (int j = 1; j * j <= i; ++j)
        {
            if (i % j) continue;
            a[j] ^= 1; if (j * j != i) a[i / j] ^= 1;
        }
        ++m;
    }
    if (m <= k) return printf("%lld\n", 1ll * m * fac % mod), 0;
    d[n] = 1;
    for (int i = n - 1, inv; i >= k; --i) 
    {
        inv = ksm(i, mod - 2);
        d[i] = (1ll * inv * (n - i) % mod * d[i + 1] % mod + 1ll * inv * n % mod) % mod;
    }
    f[k] = k;
    for (int i = k + 1; i <= m; ++i) f[i] = (f[i - 1] + d[i]) % mod;
    f[m] = 1ll * f[m] * fac % mod; printf("%d\n", f[m]); return 0; 
}

P3749 [六省聯考 2017] 壽司餐廳

一家餐廳出售 \(n\) 種壽司,每種壽司有一個代號 \(a_i\),每次購買壽司的種類必須形成一段區間。吃掉區間 \([l,r]\) 內的壽司會產生 \(d_{l,r}\) 的美味度,但每種美味度在最終計算中只會計入一次。餐廳的收費方式是如果吃了 \(c\) 種代號為 \(x\) 的壽司,則需要支付 \(mx^2+cx\) 元。求能獲得的美味度減去減去花費的最大值。(\(1\le n\le 100,1\le a_i\le 10^3,m\in\{0,1\},|d_{i,j}|\le 500\))

\(n\) 真的很小,很小,再加上必須選的是一個區間的限制可以轉化為 \(d_{l,r}\) 選了,則 \([l,r]\) 的所有子區間都要選,所以最大權閉合子圖,求法就用網路流。

我們來總結一下存在哪些型別的有向邊作為限制。

  • 收費限制,即對於每種壽司 \(i\),都要有一條有向邊指向一個 互不相同 的虛點 \(x_{i,a_i}\),保證每個壽司都要收費。
  • 代號限制,即對於每種壽司 \(i\),都要有一條有向邊指向一個 唯一 的虛點 \(y_{a_i}\),保證對應代號的壽司只收一次。
  • 區間限制,對於表示選區間 \([l,r]\) 的點,都要有兩條有向邊分別指向 \([l,r-1],[l-1,r]\)。特別地,區間 \([i,i]\) 即為第 \(i\) 種壽司對應的點。

接下來考慮每種點的權值。

  • 區間 \([l,r]\) 點,權值顯然為 \(d_{l,r}\)
  • 虛點 \(y_{a_i}\),權值是 \(-m{a_i}^2\),表示收費中的 \(mx^2\) 部分。
  • 虛點 \(x_{i,a_i}\),權值是 \(-a_i\),表示收費中的 \(cx\) 部分。

然後就是套網路流求最大權閉合子圖的板子了,時間複雜度是求一遍網路流的複雜度。我的程式碼還把 \(a_i\) 離散化了一下,不過沒必要。

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N = 5e6 + 10, M = 110, inf = 2e9; int a[M], b[M], rev[M], tmp[M], id[M][M], ID[M], len;
struct edge{ int v, c, next; }E[N << 1]; int cur[N], p[N], cnt, S, T = N - 1, tn, d[N];
inline void init() { memset(p, -1, sizeof (p)); memset(cur, -1, sizeof (cur)); cnt = 0; }
inline void insert(int u, int v, int c) { E[cnt].v = v; E[cnt].c = c; E[cnt].next = p[u]; p[u] = cnt++; }
inline void addedge(int u, int v, int c) { insert(u, v, c); insert(v, u, 0); } 
inline bool bfs()
{
    memset(d, -1, sizeof (int) * (tn + 10)); d[T] = -1;
    std::queue<int> q; q.push(S); d[S] = 0; cur[S] = p[S];
    while (!q.empty())
    {
        int u = q.front(); q.pop();
        for (int i = p[u], v; i + 1; i = E[i].next)
        {
            v = E[i].v; cur[v] = p[v];
            if (d[v] == -1 && E[i].c) d[v] = d[u] + 1, q.push(v);
        }
    }
    return d[T] != -1;
}
int dfs(int u, int flow)
{
    if (u == T) return flow; int ans = 0, ret;
    for (int i = cur[u], v; i + 1; i = E[i].next)
    {
        v = E[i].v; cur[u] = i;
        if (d[v] == d[u] + 1 && E[i].c)
        {
            ret = dfs(v, std::min(E[i].c, flow));
            E[i].c -= ret; E[i ^ 1].c += ret; 
            flow -= ret; ans += ret; if (!flow) break;
        }
    }
    if (!ans) d[u] = -1; return ans;
}
int dinic() { int ans = 0; while (bfs()) ans += dfs(S, inf); return ans; }
int main()
{
    init(); int n, m, ans = 0; scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), tmp[i] = a[i];
    std::sort(tmp + 1, tmp + 1 + n); len = std::unique(tmp + 1, tmp + 1 + n) - tmp - 1;
    for (int i = 1; i <= n; ++i) b[i] = std::lower_bound(tmp + 1, tmp + 1 + len, a[i]) - tmp, rev[b[i]] = a[i];
    for (int i = 1; i <= len; ++i) addedge(++tn, T, m * rev[i] * rev[i]);
    for (int i = 1; i <= n; ++i) addedge(ID[i] = ++tn, T, a[i]), addedge(ID[i], b[i], inf);
    for (int i = 1, d; i <= n; ++i)
        for (int j = 1; j <= n - i + 1; ++j)
        {
            scanf("%d", &d); 
            if (d < 0) addedge(!id[i][j] ? id[i][j] = ++tn : id[i][j], T, -d);
            else addedge(S, !id[i][j] ? id[i][j] = ++tn : id[i][j], d), ans += d;
            if (j == 1) addedge(id[i][j], ID[i], inf);
            else
            {
                addedge(id[i][j], id[i][j - 1], inf); 
                addedge(id[i][j], (!id[i + 1][j - 1] ? (id[i + 1][j - 1] = ++tn) : id[i + 1][j - 1]), inf);
            }
        }
    printf("%d\n", ans - dinic()); return 0;
}