Problem:
問題 H : キャッシュ戦略
今，G○○gle Code Jam の地区大会が始まろうとしている．
斜め右前の席に座っている男の ID は lyrically と言うらしい．
東京大学時代の記憶に，似たような ID の仲間が居た覚えがあるが，僕の仲間は一人残さず美少女だったはずだ．
僕の記憶の中の lyrically は，アルゴリズムの力や実装の力もさながら，
気合で問題に正解することにも定評があった．
例えば，計算量が多少悪いプログラムでも，上手な実装をすることで高速にし，
正解とするようなことも得意としていた．
プログラムを高速にする上で，非常に大切になってくるのが，
キャッシュメモリとの親和性である．
問題
ブロック毎に読み込みのコストが異なるメモリを考える．
起こる全てのメモリアクセスがあらかじめ分かった状態での，
フルアソシアティブのキャッシュメモリでの最善なキャッシュ戦略を求めたい．
以下，平易な言葉で説明する．
M
個の箱と，
N
個のボールがある．
ボールには
1
から
N
までの番号が付いており，
各ボール
i
には重さ
w
i
が決まっている．
また，長さ
K
の数列
a
1
,
a
2
, …,
a
K
が与えられる．
各
a
j
は
1 ≤
a
j
≤
N
を満たす整数である．
はじめは全ての箱は空である．
j
= 1, 2, …,
K
の順に，以下を行いたい．
ボール
a
j
が入っている箱があれば，何もしない．
この操作にかかるコストは 0 である．
ボール
a
j
が入っている箱がなければ，いずれかの箱にボール
a
j
を入れる．
ボールを箱に入れる際，もし既にその箱に入っているボールがあれば，そのボールは外に出す．
この操作にかかるコストは
w
a
j
である．（コストは，入れる箱や取り出すボール等にはよらない．）
コストの和の最小値を計算するプログラムを作成せよ．
入力
入力の最初の行は 3 つの整数
M
,
N
,
K
を含む．
続く
N
行の
i
行目には整数
w
i
が書かれている．
続く
K
行の
j
行目には整数
a
j
が書かれている．
出力
コストの和の最小値を出力せよ．
制約
1 ≤
M
≤ 10
1 ≤
N
≤ 10
4
1 ≤
K
≤ 10
4
1 ≤
w
i
≤ 10
4
1 ≤
a
j
≤
N
部分点
この問題の判定には，20 点分のテストケースのグループが設定されている．
このグループに含まれるテストケースの入力は以下を満たす．
1 ≤
M
≤ 3
1 ≤
N
≤ 10
1 ≤
K
≤ 10
3
入出力例
入力例 1
入力例 1:
3 3 6
10
20
30
1
2
3
1
2
3
入力例 1 に対する出力例:
60
入力例 2
入力例 2:
2 3 6
10
20
30
1
2
3
1
2
3
入力例 2 に対する出力例:
80

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 10005;
const int M = N * 4;

int n, m, k;
int w[N];
int a[N];
int lst[N];

typedef int ftype;
const ftype INF = 0x3f3f3f3f;
int head[N], to[M], nxt[M], tot, cur[N];
ftype cap[M], cost[M], flow[N], dis[N], mncost, mxflow;
bool vis[N];
inline void init() { tot = 0; memset(head, -1, sizeof(head)); }
inline void addedge(int x, int y, ftype w, ftype c) {
  to[tot] = y; cap[tot] = w; cost[tot] = c; nxt[tot] = head[x]; head[x] = tot++;
  to[tot] = x; cap[tot] = 0; cost[tot] = -c; nxt[tot] = head[y]; head[y] = tot++;
}
bool SPFA(int src, int sink) {
  memset(dis, 0x3f, sizeof(dis));
  memset(vis, 0, sizeof(vis));
  dis[src] = 0; cur[src] = -1; flow[src] = INF;
  queue<int> que; que.push(src);
  while (!que.empty()) {
    int u = que.front(); que.pop();
    vis[u] = false;
    for (int i = head[u], v; ~i; i = nxt[i]) {
      v = to[i];
      if (cap[i] > 0 && dis[v] > dis[u] + cost[i]) {
        dis[v] = dis[u] + cost[i]; flow[v] = min(flow[u], cap[i]); cur[v] = i;
        if (!vis[v]) { vis[v] = true; que.push(v); }
      }
    }
  }
  if (dis[sink] == INF) { return false; }
  mxflow += flow[sink]; mncost += flow[sink] * dis[sink];
  for (int i = cur[sink]; ~i; i = cur[to[i ^ 1]]) {
    cap[i] -= flow[sink]; cap[i ^ 1] += flow[sink];
  }
  return true;
}
ftype MCMF(int src, int sink) {
  mxflow = mncost = 0;
  while (SPFA(src, sink));
  return mncost;
}

int main() {
  while (~scanf("%d%d%d", &m, &n, &k)) {
    init(); memset(lst, 0, sizeof(lst));
    int sum = 0; m--;
    for (int i = 1; i <= n; i++) {
      scanf("%d", &w[i]);
    }
    for (int i = 1; i <= k; i++) {
      scanf("%d", &a[i]);
    }
    k = unique(a + 1, a + k + 1) - a - 1;
    for (int i = 1; i <= k; i++) {
      if (lst[a[i]]) { addedge(lst[a[i]], i - 1, 1, -w[a[i]]); }
      lst[a[i]] = i;
      sum += w[a[i]];
    }
    for (int i = 2; i <= k; i++) {
      addedge(i - 1, i, m, 0);
    }
    int src = k + 1, sink = src + 1;
    addedge(src, 1, m, 0);
    addedge(k, sink, m, 0);
    printf("%d\n", sum + MCMF(src, sink));
  }
}
accuracy: 1
time_ms: 1280
memory_kb: 4448
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct edge{
  edge(int to_,int wt_,int cp_,int rev_):to(to_),wt(wt_),cp(cp_),rev(rev_){}
  int to,wt,cp,rev;
};

vector<vector<edge>> g(10005);
vector<int> a(10005);
vector<vector<int>> lst(10005);
vector<int> w(10005);
vector<int> prv(10005),pre(10005);
int m,n,k;

void adde(int f1,int t1,int w1,int c1){
  g[f1].push_back(edge(t1,w1,c1,g[t1].size()));
  g[t1].push_back(edge(f1,-w1,0,g[f1].size()-1));
}

int mincost(int stt,int stp,int flw){
  int ret=0;
  while(flw>0){
    vector<int> gm(k+2,INT_MAX);
    gm[stt]=0;
    bool flg=true;
    while(flg){
      flg=false;
      for(int i=0;i<k+2;i++){
        if(gm[i]==INT_MAX) continue;
        for(int j=0;j<(int)g[i].size();j++){
          auto e=g[i][j];
          if(e.cp==0) continue;
          if(gm[e.to]>gm[i]+e.wt){ 
            gm[e.to]=gm[i]+e.wt;
            prv[e.to]=i;
            pre[e.to]=j;
            flg=true;
          }
        }
        
      }
    }
    int tmp=flw;
    for(int i=stp;i!=stt;i=prv[i]){
      tmp=min(tmp,g[prv[i]][pre[i]].cp);
    }
    
    flw-=tmp;
    ret+=tmp*gm[stp];
    for(int i=stp;i!=stt;i=prv[i]){
      g[prv[i]][pre[i]].cp-=tmp;
      g[i][g[prv[i]][pre[i]].rev].cp+=tmp;
    }
    
  }
  return ret;
}

int main(){
  cin >> m >> n >> k;
  for(int i=0;i<n;i++) cin >> w[i];
  int ans=0;
  for(int i=0;i<k;i++){ 
    cin >> a[i];
    a[i]--;
    ans+=w[a[i]];
    lst[a[i]].push_back(i);
  }
  
  for(int i=0;i<k;i++){
    if((i>0)&&(a[i-1]==a[i])) continue;
  }
  
  int stt=k,stp=k+1;
  adde(stt,0,0,m);
  adde(k-1,stp,0,m);
  for(int i=0;i<k-1;i++) adde(i,i+1,0,m);
  
  for(int i=0;i<n;i++){
    if(lst[i].size()<=1) continue;
    for(int j=0;j<(int)lst[i].size()-1;j++){
      if(lst[i][j]+1==lst[i][j+1]){
        ans-=w[i];
        continue;
      }
      adde(lst[i][j]+1,lst[i][j+1],-w[i],1);
    }
  }
  cout << ans+mincost(k,k+1,m-1) << endl;
}
accuracy: 1
time_ms: 920
memory_kb: 4888
final_rank: 8

code_snippet: #include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <utility>
using namespace std;
const int INF=0x3f3f3f3f;
typedef long long LL;
struct edge{
    int to,cap,cost,rev;
    edge(int to,int cap,int cost,int rev):to(to),cap(cap),cost(cost),rev(rev){}
};
const int MAX_V=10010;
int V,dist[MAX_V],prevv[MAX_V],preve[MAX_V];
vector<edge> G[MAX_V];
void add_edge(int from,int to,int cap,int cost){
    G[from].push_back(edge(to,cap,cost,G[to].size()));
    G[to].push_back(edge(from,0,-cost,G[from].size()-1));
}
int min_cost_flow(int s,int t,int f){
    int res=0;
    while(f>0){
        fill(dist,dist+V,INF);
        dist[s]=0;
        bool update=1;
        while(update){
            update=0;
            for(int v=0;v<V;v++){
                if(dist[v]==INF)continue;
                for(int i=0;i<G[v].size();i++){
                    edge &e=G[v][i];
                    if(e.cap>0&&dist[e.to]>dist[v]+e.cost){
                        dist[e.to]=dist[v]+e.cost;
                        prevv[e.to]=v;
                        preve[e.to]=i;
                        update=1;
                    }
                }
            }
        }
        if(dist[t]==INF)return -1;
        int d=f;
        for(int v=t;v!=s;v=prevv[v]){
            d=min(d,G[prevv[v]][preve[v]].cap);
        }f-=d;
        res+=d*dist[t];
        for(int v=t;v!=s;v=prevv[v]){
            edge &e=G[prevv[v]][preve[v]];
            e.cap-=d;
            G[v][e.rev].cap+=d;
        }
    }return res;
}
void clear(){for(int i=0;i<=V;i++)G[i].clear();}
const int MAX_N=10010;
int M,N,K,w[MAX_N],lst[MAX_N],a[MAX_N];
int solve(){
    for(int i=1;i<=N;i++)scanf("%d",&w[i]);
    for(int i=1;i<=K;i++)scanf("%d",&a[i]);
    int cnt=unique(a+1,a+K+1)-(a+1);
    int res=0;
    memset(lst,0,sizeof(lst));
    V=cnt+1; clear();
    for(int i=1;i<=cnt;i++){
        res+=w[a[i]];
        if(lst[a[i]])add_edge(lst[a[i]],i-1,1,-w[a[i]]);
        lst[a[i]]=i;
    }for(int i=1;i<cnt;i++)add_edge(i,i+1,INF,0);
    printf("%d\n",res+min_cost_flow(1,cnt,M-1));
return 1;
}
int main(){
    while(~scanf("%d%d%d",&M,&N,&K))solve();
    return 0;
}
accuracy: 1
time_ms: 560
memory_kb: 4264
final_rank: 7

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// https://area.hateblo.jp/entry/2017/12/18/010657

// Minimum Cost Flow (Dijkstra)
// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_6_B&lang=ja
// 蟻本 p. 203
// 始点0, 終点V-1

int V, E, F;
struct edge { int to, cap, cost, rev; };
typedef pair<int, int> P;

vector<edge> G[MAX_N];
int h[MAX_N];
int dist[MAX_N];
int prevv[MAX_N], preve[MAX_N];


// from からtoへ向かう容量cap, コストcostの辺をグラフに追加する
void add_edge(int from, int to, int cap, int cost) {
	edge e1 = { to, cap, cost, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, -cost, G[from].size() - 1 };
	G[to].push_back(e2);

}

// sからtへの流量f最小費用流を求める
// 流せない場合は-1を返す
int min_cost_flow(int s, int t, int f) {
	int res = 0;
	while (f > 0) {
		//ベルマンフォードにより、s-t間最短路を求める
		fill(dist, dist + V, INF);
		dist[s] = 0;
		bool update = true;
		while (update) {
			update = false;
			for (int v = 0; v < V; v++) {
				if (dist[v] == INF) {
					continue;
				}
				for (int i = 0; i < G[v].size(); i++) {
					edge& e = G[v][i];
					if (e.cap > 0 && dist[e.to] > dist[v] + e.cost) {
						dist[e.to] = dist[v] + e.cost;
						prevv[e.to] = v;
						preve[e.to] = i;
						update = true;
					}
				}
			}
		}
		if (dist[t] == INF) {
			return -1;
		}

		// s-t間最短路に沿って目一杯流す。
		int d = f;
		for (int v = t; v != s; v = prevv[v]) {
			d = min(d, G[prevv[v]][preve[v]].cap);
		}

		f -= d;
		res += (d * dist[t]);
		for (int v = t; v != s; v = prevv[v]) {
			edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}

	return res;

}

int M, N, K;
int w[10010];
int a[10010];
int idx[10010];

int main() {

	cin >> M >> N >> K;

	for (int i = 0; i < N; i++) {
		cin >> w[i];
	}
	for (int i = 0; i < K; i++) {
		cin >> a[i];
		a[i]--;
	}
	vector<int> v;
	v.push_back(a[0]);
	for (int i = 1; i < K; i++) {
		if (v[v.size() - 1] != a[i]) {
			v.push_back(a[i]);
		}
	}
	
	V = v.size();

	for (int i = 0; i < N; i++) {
		idx[i] = -1;
	}

	for (int i = 0; i < V - 1; i++) {
		add_edge(i, i + 1, INF, 0);
	}

	int sum = 0;
	for (int i = 0; i < v.size(); i++) {
		sum += w[v[i]];
		if (idx[v[i]] == -1) {
			idx[v[i]] = i;
		}
		else {
			add_edge(idx[v[i]] + 1, i, 1, -w[v[i]]);
			idx[v[i]] = i;
		}
	}

	cout << sum + min_cost_flow(0, V - 1, M - 1) << endl;

	return 0;
}
accuracy: 1
time_ms: 650
memory_kb: 51112
final_rank: 12

code_snippet: #ifndef HIDDEN_IN_VS // 折りたたみ用

// 警告の抑制
#define _CRT_SECURE_NO_WARNINGS

// ライブラリの読み込み
#include <bits/stdc++.h>
using namespace std;

// 型名の短縮
using ll = long long; // -2^63 ～ 2^63 = 9 * 10^18（int は -2^31 ～ 2^31 = 2 * 10^9）
using pii = pair<int, int>;	using pll = pair<ll, ll>;	using pil = pair<int, ll>;	using pli = pair<ll, int>;
using vi = vector<int>;		using vvi = vector<vi>;		using vvvi = vector<vvi>;
using vl = vector<ll>;		using vvl = vector<vl>;		using vvvl = vector<vvl>;
using vb = vector<bool>;	using vvb = vector<vb>;		using vvvb = vector<vvb>;
using vc = vector<char>;	using vvc = vector<vc>;		using vvvc = vector<vvc>;
using vd = vector<double>;	using vvd = vector<vd>;		using vvvd = vector<vvd>;
template <class T> using priority_queue_rev = priority_queue<T, vector<T>, greater<T>>;
using Graph = vvi;

// 定数の定義
const double PI = acos(-1);
const vi DX = { 1, 0, -1, 0 }; // 4 近傍（下，右，上，左）
const vi DY = { 0, 1, 0, -1 };
int INF = 1001001001; ll INFL = 4004004004004004004LL;
double EPS = 1e-12;

// 入出力高速化
struct fast_io { fast_io() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(18); } } fastIOtmp;

// 汎用マクロの定義
#define all(a) (a).begin(), (a).end()
#define sz(x) ((int)(x).size())
#define lbpos(a, x) (int)distance((a).begin(), std::lower_bound(all(a), x))
#define ubpos(a, x) (int)distance((a).begin(), std::upper_bound(all(a), x))
#define Yes(b) {cout << ((b) ? "Yes\n" : "No\n");}
#define rep(i, n) for(int i = 0, i##_len = int(n); i < i##_len; ++i) // 0 から n-1 まで昇順
#define repi(i, s, t) for(int i = int(s), i##_end = int(t); i <= i##_end; ++i) // s から t まで昇順
#define repir(i, s, t) for(int i = int(s), i##_end = int(t); i >= i##_end; --i) // s から t まで降順
#define repe(v, a) for(const auto& v : (a)) // a の全要素（変更不可能）
#define repea(v, a) for(auto& v : (a)) // a の全要素（変更可能）
#define repb(set, d) for(int set = 0; set < (1 << int(d)); ++set) // d ビット全探索（昇順）
#define repp(a) sort(all(a)); for(bool a##_perm = true; a##_perm; a##_perm = next_permutation(all(a))) // a の順列全て（昇順）
#define smod(n, m) ((((n) % (m)) + (m)) % (m)) // 非負mod
#define uniq(a) {sort(all(a)); (a).erase(unique(all(a)), (a).end());} // 重複除去
#define EXIT(a) {cout << (a) << endl; exit(0);} // 強制終了

// 汎用関数の定義
template <class T> inline ll pow(T n, int k) { ll v = 1; rep(i, k) v *= n; return v; }
template <class T> inline bool chmax(T& M, const T& x) { if (M < x) { M = x; return true; } return false; } // 最大値を更新（更新されたら true を返す）
template <class T> inline bool chmin(T& m, const T& x) { if (m > x) { m = x; return true; } return false; } // 最小値を更新（更新されたら true を返す）

// 演算子オーバーロード
template <class T, class U> inline istream& operator>>(istream& is, pair<T, U>& p) { is >> p.first >> p.second; return is; }
template <class T> inline istream& operator>>(istream& is, vector<T>& v) { repea(x, v) is >> x; return is; }
template <class T> inline vector<T>& operator--(vector<T>& v) { repea(x, v) --x; return v; }
template <class T> inline vector<T>& operator++(vector<T>& v) { repea(x, v) ++x; return v; }

// 手元環境（Visual Studio）
#ifdef _MSC_VER
#include "local.hpp"
// 提出用（gcc）
#else
inline int popcount(int n) { return __builtin_popcount(n); }
inline int popcount(ll n) { return __builtin_popcountll(n); }
inline int lsb(int n) { return n != 0 ? __builtin_ctz(n) : -1; }
inline int lsb(ll n) { return n != 0 ? __builtin_ctzll(n) : -1; }
inline int msb(int n) { return n != 0 ? (31 - __builtin_clz(n)) : -1; }
inline int msb(ll n) { return n != 0 ? (63 - __builtin_clzll(n)) : -1; }
#define gcd __gcd
#define dump(...)
#define dumpel(v)
#define dump_list(v)
#define input_from_file(f)
#define output_to_file(f)
#define Assert(b) { if (!(b)) while (1) cout << "OLE"; }
#endif

#endif // 折りたたみ用


////--------------AtCoder 専用--------------
//#include <atcoder/all>
//using namespace atcoder;
//
////using mint = modint1000000007;
//using mint = modint998244353;
////using mint = modint; // mint::set_mod(m);
//
//istream& operator>>(istream& is, mint& x) { ll x_; is >> x_; x = x_; return is; }
//ostream& operator<<(ostream& os, const mint& x) { os << x.val(); return os; }
//using vm = vector<mint>; using vvm = vector<vm>; using vvvm = vector<vvm>;
////----------------------------------------


#ifndef ATCODER_INTERNAL_CSR_HPP
#define ATCODER_INTERNAL_CSR_HPP 1

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
    namespace internal {

        template <class E> struct csr {
            std::vector<int> start;
            std::vector<E> elist;
            explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
                : start(n + 1), elist(edges.size()) {
                for (auto e : edges) {
                    start[e.first + 1]++;
                }
                for (int i = 1; i <= n; i++) {
                    start[i] += start[i - 1];
                }
                auto counter = start;
                for (auto e : edges) {
                    elist[counter[e.first]++] = e.second;
                }
            }
        };

    }  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_CSR_HPP


#ifndef ATCODER_INTERNAL_QUEUE_HPP
#define ATCODER_INTERNAL_QUEUE_HPP 1

#include <vector>

namespace atcoder {

    namespace internal {

        template <class T> struct simple_queue {
            std::vector<T> payload;
            int pos = 0;
            void reserve(int n) { payload.reserve(n); }
            int size() const { return int(payload.size()) - pos; }
            bool empty() const { return pos == int(payload.size()); }
            void push(const T& t) { payload.push_back(t); }
            T& front() { return payload[pos]; }
            void clear() {
                payload.clear();
                pos = 0;
            }
            void pop() { pos++; }
        };

    }  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_QUEUE_HPP


#ifndef ATCODER_MINCOSTFLOW_HPP
#define ATCODER_MINCOSTFLOW_HPP 1

#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

    template <class Cap, class Cost> struct mcf_graph {
    public:
        mcf_graph() {}
        explicit mcf_graph(int n) : _n(n) {}

        int add_edge(int from, int to, Cap cap, Cost cost) {
            assert(0 <= from && from < _n);
            assert(0 <= to && to < _n);
            assert(0 <= cap);
            assert(0 <= cost);
            int m = int(_edges.size());
            _edges.push_back({ from, to, cap, 0, cost });
            return m;
        }

        struct edge {
            int from, to;
            Cap cap, flow;
            Cost cost;
        };

        edge get_edge(int i) {
            int m = int(_edges.size());
            assert(0 <= i && i < m);
            return _edges[i];
        }
        std::vector<edge> edges() { return _edges; }

        std::pair<Cap, Cost> flow(int s, int t) {
            return flow(s, t, std::numeric_limits<Cap>::max());
        }
        std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
            return slope(s, t, flow_limit).back();
        }
        std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
            return slope(s, t, std::numeric_limits<Cap>::max());
        }
        std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
            assert(0 <= s && s < _n);
            assert(0 <= t && t < _n);
            assert(s != t);

            int m = int(_edges.size());
            std::vector<int> edge_idx(m);

            auto g = [&]() {
                std::vector<int> degree(_n), redge_idx(m);
                std::vector<std::pair<int, _edge>> elist;
                elist.reserve(2 * m);
                for (int i = 0; i < m; i++) {
                    auto e = _edges[i];
                    edge_idx[i] = degree[e.from]++;
                    redge_idx[i] = degree[e.to]++;
                    elist.push_back({ e.from, {e.to, -1, e.cap - e.flow, e.cost} });
                    elist.push_back({ e.to, {e.from, -1, e.flow, -e.cost} });
                }
                auto _g = internal::csr<_edge>(_n, elist);
                for (int i = 0; i < m; i++) {
                    auto e = _edges[i];
                    edge_idx[i] += _g.start[e.from];
                    redge_idx[i] += _g.start[e.to];
                    _g.elist[edge_idx[i]].rev = redge_idx[i];
                    _g.elist[redge_idx[i]].rev = edge_idx[i];
                }
                return _g;
            }();

            auto result = slope(g, s, t, flow_limit);

            for (int i = 0; i < m; i++) {
                auto e = g.elist[edge_idx[i]];
                _edges[i].flow = _edges[i].cap - e.cap;
            }

            return result;
        }

    private:
        int _n;
        std::vector<edge> _edges;

        // inside edge
        struct _edge {
            int to, rev;
            Cap cap;
            Cost cost;
        };

        std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
            int s,
            int t,
            Cap flow_limit) {
            // variants (C = maxcost):
            // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
            // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge

            // dual_dist[i] = (dual[i], dist[i])
            std::vector<std::pair<Cost, Cost>> dual_dist(_n);
            std::vector<int> prev_e(_n);
            std::vector<bool> vis(_n);
            struct Q {
                Cost key;
                int to;
                bool operator<(Q r) const { return key > r.key; }
            };
            std::vector<int> que_min;
            std::vector<Q> que;
            auto dual_ref = [&]() {
                for (int i = 0; i < _n; i++) {
                    dual_dist[i].second = std::numeric_limits<Cost>::max();
                }
                std::fill(vis.begin(), vis.end(), false);
                que_min.clear();
                que.clear();

                // que[0..heap_r) was heapified
                size_t heap_r = 0;

                dual_dist[s].second = 0;
                que_min.push_back(s);
                while (!que_min.empty() || !que.empty()) {
                    int v;
                    if (!que_min.empty()) {
                        v = que_min.back();
                        que_min.pop_back();
                    }
                    else {
                        while (heap_r < que.size()) {
                            heap_r++;
                            std::push_heap(que.begin(), que.begin() + heap_r);
                        }
                        v = que.front().to;
                        std::pop_heap(que.begin(), que.end());
                        que.pop_back();
                        heap_r--;
                    }
                    if (vis[v]) continue;
                    vis[v] = true;
                    if (v == t) break;
                    // dist[v] = shortest(s, v) + dual[s] - dual[v]
                    // dist[v] >= 0 (all reduced cost are positive)
                    // dist[v] <= (n-1)C
                    Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                    for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                        auto e = g.elist[i];
                        if (!e.cap) continue;
                        // |-dual[e.to] + dual[v]| <= (n-1)C
                        // cost <= C - -(n-1)C + 0 = nC
                        Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                        if (dual_dist[e.to].second - dist_v > cost) {
                            Cost dist_to = dist_v + cost;
                            dual_dist[e.to].second = dist_to;
                            prev_e[e.to] = e.rev;
                            if (dist_to == dist_v) {
                                que_min.push_back(e.to);
                            }
                            else {
                                que.push_back(Q{ dist_to, e.to });
                            }
                        }
                    }
                }
                if (!vis[t]) {
                    return false;
                }

                for (int v = 0; v < _n; v++) {
                    if (!vis[v]) continue;
                    // dual[v] = dual[v] - dist[t] + dist[v]
                    //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) +
                    //         (shortest(s, v) + dual[s] - dual[v]) = - shortest(s,
                    //         t) + dual[t] + shortest(s, v) = shortest(s, v) -
                    //         shortest(s, t) >= 0 - (n-1)C
                    dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
                }
                return true;
            };
            Cap flow = 0;
            Cost cost = 0, prev_cost_per_flow = -1;
            std::vector<std::pair<Cap, Cost>> result = { {Cap(0), Cost(0)} };
            while (flow < flow_limit) {
                if (!dual_ref()) break;
                Cap c = flow_limit - flow;
                for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                    c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
                }
                for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                    auto& e = g.elist[prev_e[v]];
                    e.cap += c;
                    g.elist[e.rev].cap -= c;
                }
                Cost d = -dual_dist[s].first;
                flow += c;
                cost += c * d;
                if (prev_cost_per_flow == d) {
                    result.pop_back();
                }
                result.push_back({ flow, cost });
                prev_cost_per_flow = d;
            }
            return result;
        }
    };

}  // namespace atcoder

#endif  // ATCODER_MINCOSTFLOW_HPP


using namespace atcoder;


//【最小費用流（負コスト可，負コスト閉路なし）】
/*
* Negative_mcf_graph(int n) : O(1)
*	n 頂点で初期化する．
*
* add_edge(int s, int t, ll cap, ll cost) : O(1)
*	s から t へ容量 cap，コスト cost の辺を追加する．
*
* pll flow(int ST, int GL, ll f_lim = INFL) : O(F (n + m) log n)（F:流量，m:辺の数）
*	ST から GL まで flow_limit まで流せるだけ流したときの {流量, 最小コスト} を返す．
*	制約：負コストの閉路は存在しない
*/
struct Negative_mcf_graph {
	// 参考 : https://ikatakos.com/pot/programming_algorithm/graph_theory/minimum_cost_flow

	// n : 頂点数
	int n;

	// 辺
	struct Edge {
		int to;
		ll cap, cost;

		Edge(int to_, ll cap_, ll cost_) : to(to_), cap(cap_), cost(cost_) {}

#ifdef _MSC_VER
		friend ostream& operator<<(ostream& os, const Edge& e) {
			os << "(to:" << e.to << ", cap:" << e.cap << ", cost:" << e.cost << ")";
			return os;
		}
#endif
	};

	// 元のグラフ（負辺あり）
	vector<vector<Edge>> g;

	// pot[s] : 頂点 s のポテンシャル
	vl pot;

	// n 頂点で初期化する．
	Negative_mcf_graph(int n_) : n(n_), g(n), pot(n, INFL) {}

	// s から t へ容量 cap，コスト cost の辺を追加する．
	void add_edge(int s, int t, ll cap, ll cost) {
		// verify : https://atcoder.jp/contests/code-festival-2014-china-open/tasks/code_festival_china_h

		g[s].emplace_back(t, cap, cost);
	}

	// ベルマンフォード法で ST からの距離を求め，それをポテンシャルとする．
	bool bellman_ford(int ST) {
		pot[ST] = 0;

		rep(i, n) {
			bool updated = false;

			// 全ての辺についての操作
			rep(s, n) repe(e, g[s]) {
				// もし (始点への距離) + (辺のコスト) < (終点への距離) なら (終点への距離) を更新する．
				// INFL からは何を引いても INFL になるようにしているので，ST から到達可能な負閉路しか検出しない．
				if (pot[s] != INFL && pot[s] + e.cost < pot[e.to]) {
					pot[e.to] = pot[s] + e.cost;
					updated = true;
				}
			}

			// もし距離の更新が起こらなければ最短距離確定
			if (!updated) return true;
		}

		// もし全ての辺についての操作を n 回繰り返しても距離の更新があったなら，
		// ST から到達可能な負の閉路を持っているので false を返す．
		return false;
	}

	// ST から GL まで flow_limit まで流せるだけ流したときの {流量, 最小コスト} を返す．
	pll flow(int ST, int GL, ll f_lim = INFL) {
		// verify : https://atcoder.jp/contests/code-festival-2014-china-open/tasks/code_festival_china_h

		// ベルマンフォード法で ST から v までの距離を求め，それをポテンシャル pot[v] とする．
		bool non_neg_cyc = bellman_ford(ST);
		Assert(non_neg_cyc);

		// g_pos : 辺 s→t のコストが (元の辺のコスト) - (pot[t] - pot[s]) >= 0 であるようなグラフ
		mcf_graph<ll, ll> g_pos(n);
		rep(s, n) repe(e, g[s]) {
			g_pos.add_edge(s, e.to, e.cap, e.cost - (pot[e.to] - pot[s]));
		}

		// g_pos の最小費用流を求める．
		ll cap, cost;
		tie(cap, cost) = g_pos.flow(ST, GL, f_lim);

		// 実際のコストは (流量) * pot[GL] を加えたものになる．
		cost += cap * pot[GL];

		return make_pair(cap, cost);
	}
};


ll MLE(int m, int n, int k, vi w, vi a) {
    --a;

    vvi nxt(k + 1, vi(n, k));
    repir(i, k - 1, 0) {
        rep(c, n) {
            nxt[i][c] = nxt[i + 1][c];
        }
        nxt[i][a[i]] = i;
    }
    dumpel(nxt);

    int ST = 2 * k, GL = ST + 1;
    Negative_mcf_graph g(GL + 1);

    rep(i, n) {
        if (nxt[0][i] < k) {
            g.add_edge(ST, 2 * nxt[0][i], 1, w[i]);
        }
        else {
            g.add_edge(ST, GL, 1, 0);
        }
    }

    rep(t, k) {
        g.add_edge(2 * t, 2 * t + 1, 1, -INF);

        rep(i, n) {
            if (a[t] == i) {
                if (nxt[t + 1][i] < k) {
                    g.add_edge(2 * t + 1, 2 * nxt[t + 1][i], 1, 0);
                }
                else {
                    g.add_edge(2 * t + 1, GL, 1, 0);
                }
            }
            else {
                if (nxt[t + 1][i] < k) {
                    g.add_edge(2 * t + 1, 2 * nxt[t + 1][i], 1, w[i]);
                }
            }
        }
    }

    dumpel(g.g);

    ll cap, cost;
    tie(cap, cost) = g.flow(ST, GL, m);
    dump(cap, cost);

    ll res = cost + (ll)INF * k;

    return res;
}


//【自身と同じ数の次の位置】O(n)
/*
* a[0..n) の各要素 a[i] について，j > i かつ a[j] = a[i] となる最小の j を
* nxt[j] に格納する．（存在しなければ n）
*/
template <class T> void next_equal(const vector<T>& a, vi& nxt) {
    // verify : https://atcoder.jp/contests/agc036/tasks/agc036_b

    int n = sz(a);

    // nxt[i] : j > i かつ a[j] = a[i] となる最小の j（なければ n）
    nxt = vi(n, n);

    // num_to_pos[x] : 値 x が最後に現れた位置（右から走査する）
    unordered_map<T, int> num_to_pos;

    repir(i, n - 1, 0) {
        if (num_to_pos.count(a[i])) {
            nxt[i] = num_to_pos[a[i]];
        }
        num_to_pos[a[i]] = i;
    }
}


//【最小費用流（負コスト可，DAG）】
/*
* Negative_mcf_graph(int n) : O(1)
*	n 頂点で初期化する．
*
* add_edge(int s, int t, ll cap, ll cost) : O(1)
*	s から t へ容量 cap，コスト cost の辺を追加する．
*
* pll flow(int ST, int GL, ll f_lim = INFL) : O(F (n + m) log n)（F:流量，m:辺の数）
*	ST から GL まで flow_limit まで流せるだけ流したときの {流量, 最小コスト} を返す．
*	制約：閉路は存在しない
*/
struct Negative_mcf_graph_DAG {
    // 参考 : https://ikatakos.com/pot/programming_algorithm/graph_theory/minimum_cost_flow

    // n : 頂点数
    int n;

    // 辺
    struct Edge {
        int to;
        ll cap, cost;

        Edge(int to_, ll cap_, ll cost_) : to(to_), cap(cap_), cost(cost_) {}

#ifdef _MSC_VER
        friend ostream& operator<<(ostream& os, const Edge& e) {
            os << "(to:" << e.to << ", cap:" << e.cap << ", cost:" << e.cost << ")";
            return os;
        }
#endif
    };

    // 元のグラフ（負辺あり）
    vector<vector<Edge>> g;

    // pot[s] : 頂点 s のポテンシャル
    vl pot;

    // n 頂点で初期化する．
    Negative_mcf_graph_DAG(int n_) : n(n_), g(n), pot(n, INFL) {}

    // s から t へ容量 cap，コスト cost の辺を追加する．
    void add_edge(int s, int t, ll cap, ll cost) {
        // verify : https://onlinejudge.u-aizu.ac.jp/problems/2266

        g[s].emplace_back(t, cap, cost);
    }

    // DAG 上の DP で GL までの距離を求め，その -1 倍をポテンシャルとする．
    void DAG_DP(int GL) {
        pot[GL] = 0;

        vb seen(n);
        seen[GL] = true;

        function<ll(int)> dfs = [&](int s) {
            if (seen[s]) return pot[s];
            seen[s] = true;

            repe(e, g[s]) chmin(pot[s], dfs(e.to) + e.cost);

            return pot[s];
        };

        // 各頂点 s についての情報を計算する．
        rep(s, n) if (!seen[s]) dfs(s);

        rep(s, n) pot[s] *= -1;
    }

    // ST から GL まで flow_limit まで流せるだけ流したときの {流量, 最小コスト} を返す．
    pll flow(int ST, int GL, ll f_lim = INFL) {
        // verify : https://onlinejudge.u-aizu.ac.jp/problems/2266

        // DAG 上の DP で v から GL までの距離を求め，その -1 倍をポテンシャル pot[v] とする．
        DAG_DP(GL);

        // g_pos : 辺 s→t のコストが (元の辺のコスト) - (pot[t] - pot[s]) >= 0 であるようなグラフ
        mcf_graph<ll, ll> g_pos(n);
        rep(s, n) repe(e, g[s]) {
            g_pos.add_edge(s, e.to, e.cap, e.cost - (pot[e.to] - pot[s]));
        }

        // g_pos の最小費用流を求める．
        ll cap, cost;
        tie(cap, cost) = g_pos.flow(ST, GL, f_lim);

        // 実際のコストは (流量) * (pot[GL] - pot[ST]) を加えたものになる．
        cost += cap * (pot[GL] - pot[ST]);

        return make_pair(cap, cost);
    }
};


int main() {
//	input_from_file("input.txt");
//	output_to_file("output.txt");
	
	int m, n, k;
	cin >> m >> n >> k;

	vi w(n), a(k);
	cin >> w >> a;
    --a;

    ll w_sum = 0;
    rep(i, k) w_sum += w[a[i]];

    vi nxt;
    next_equal(a, nxt);

    Negative_mcf_graph g(2 * k);

    rep(i, k) {
        g.add_edge(2 * i, 2 * i + 1, 1, -INF);
        g.add_edge(2 * i, 2 * i + 1, m, 0);

        if (i < k - 1) {
            g.add_edge(2 * i + 1, 2 * (i + 1), m, 0);
        }

        if (nxt[i] != k) {
            g.add_edge(2 * i + 1, 2 * nxt[i], 1, -w[a[i]]);
        }
    }
    dumpel(g.g);

    ll cap, cost;
    tie(cap, cost) = g.flow(2 * 0, 2 * (k - 1) + 1, m);
    dump(cap, cost);

    ll res = w_sum + cost + (ll)k * INF;

    cout << res << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 11104
final_rank: 4

code_snippet: #ifndef HIDDEN_IN_VS // 折りたたみ用

// 警告の抑制
#define _CRT_SECURE_NO_WARNINGS

// ライブラリの読み込み
#include <bits/stdc++.h>
using namespace std;

// 型名の短縮
using ll = long long; // -2^63 ～ 2^63 = 9 * 10^18（int は -2^31 ～ 2^31 = 2 * 10^9）
using pii = pair<int, int>;	using pll = pair<ll, ll>;	using pil = pair<int, ll>;	using pli = pair<ll, int>;
using vi = vector<int>;		using vvi = vector<vi>;		using vvvi = vector<vvi>;
using vl = vector<ll>;		using vvl = vector<vl>;		using vvvl = vector<vvl>;
using vb = vector<bool>;	using vvb = vector<vb>;		using vvvb = vector<vvb>;
using vc = vector<char>;	using vvc = vector<vc>;		using vvvc = vector<vvc>;
using vd = vector<double>;	using vvd = vector<vd>;		using vvvd = vector<vvd>;
template <class T> using priority_queue_rev = priority_queue<T, vector<T>, greater<T>>;
using Graph = vvi;

// 定数の定義
const double PI = acos(-1);
const vi DX = { 1, 0, -1, 0 }; // 4 近傍（下，右，上，左）
const vi DY = { 0, 1, 0, -1 };
int INF = 1001001001; ll INFL = 4004004004004004004LL;
double EPS = 1e-12;

// 入出力高速化
struct fast_io { fast_io() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(18); } } fastIOtmp;

// 汎用マクロの定義
#define all(a) (a).begin(), (a).end()
#define sz(x) ((int)(x).size())
#define lbpos(a, x) (int)distance((a).begin(), std::lower_bound(all(a), x))
#define ubpos(a, x) (int)distance((a).begin(), std::upper_bound(all(a), x))
#define Yes(b) {cout << ((b) ? "Yes\n" : "No\n");}
#define rep(i, n) for(int i = 0, i##_len = int(n); i < i##_len; ++i) // 0 から n-1 まで昇順
#define repi(i, s, t) for(int i = int(s), i##_end = int(t); i <= i##_end; ++i) // s から t まで昇順
#define repir(i, s, t) for(int i = int(s), i##_end = int(t); i >= i##_end; --i) // s から t まで降順
#define repe(v, a) for(const auto& v : (a)) // a の全要素（変更不可能）
#define repea(v, a) for(auto& v : (a)) // a の全要素（変更可能）
#define repb(set, d) for(int set = 0; set < (1 << int(d)); ++set) // d ビット全探索（昇順）
#define repp(a) sort(all(a)); for(bool a##_perm = true; a##_perm; a##_perm = next_permutation(all(a))) // a の順列全て（昇順）
#define smod(n, m) ((((n) % (m)) + (m)) % (m)) // 非負mod
#define uniq(a) {sort(all(a)); (a).erase(unique(all(a)), (a).end());} // 重複除去
#define EXIT(a) {cout << (a) << endl; exit(0);} // 強制終了

// 汎用関数の定義
template <class T> inline ll pow(T n, int k) { ll v = 1; rep(i, k) v *= n; return v; }
template <class T> inline bool chmax(T& M, const T& x) { if (M < x) { M = x; return true; } return false; } // 最大値を更新（更新されたら true を返す）
template <class T> inline bool chmin(T& m, const T& x) { if (m > x) { m = x; return true; } return false; } // 最小値を更新（更新されたら true を返す）

// 演算子オーバーロード
template <class T, class U> inline istream& operator>>(istream& is, pair<T, U>& p) { is >> p.first >> p.second; return is; }
template <class T> inline istream& operator>>(istream& is, vector<T>& v) { repea(x, v) is >> x; return is; }
template <class T> inline vector<T>& operator--(vector<T>& v) { repea(x, v) --x; return v; }
template <class T> inline vector<T>& operator++(vector<T>& v) { repea(x, v) ++x; return v; }

// 手元環境（Visual Studio）
#ifdef _MSC_VER
#include "local.hpp"
// 提出用（gcc）
#else
inline int popcount(int n) { return __builtin_popcount(n); }
inline int popcount(ll n) { return __builtin_popcountll(n); }
inline int lsb(int n) { return n != 0 ? __builtin_ctz(n) : -1; }
inline int lsb(ll n) { return n != 0 ? __builtin_ctzll(n) : -1; }
inline int msb(int n) { return n != 0 ? (31 - __builtin_clz(n)) : -1; }
inline int msb(ll n) { return n != 0 ? (63 - __builtin_clzll(n)) : -1; }
#define gcd __gcd
#define dump(...)
#define dumpel(v)
#define dump_list(v)
#define input_from_file(f)
#define output_to_file(f)
#define Assert(b) { if (!(b)) while (1) cout << "OLE"; }
#endif

#endif // 折りたたみ用


////--------------AtCoder 専用--------------
//#include <atcoder/all>
//using namespace atcoder;
//
////using mint = modint1000000007;
//using mint = modint998244353;
////using mint = modint; // mint::set_mod(m);
//
//istream& operator>>(istream& is, mint& x) { ll x_; is >> x_; x = x_; return is; }
//ostream& operator<<(ostream& os, const mint& x) { os << x.val(); return os; }
//using vm = vector<mint>; using vvm = vector<vm>; using vvvm = vector<vvm>;
////----------------------------------------


#ifndef ATCODER_INTERNAL_CSR_HPP
#define ATCODER_INTERNAL_CSR_HPP 1

#include <algorithm>
#include <utility>
#include <vector>

namespace atcoder {
    namespace internal {

        template <class E> struct csr {
            std::vector<int> start;
            std::vector<E> elist;
            explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
                : start(n + 1), elist(edges.size()) {
                for (auto e : edges) {
                    start[e.first + 1]++;
                }
                for (int i = 1; i <= n; i++) {
                    start[i] += start[i - 1];
                }
                auto counter = start;
                for (auto e : edges) {
                    elist[counter[e.first]++] = e.second;
                }
            }
        };

    }  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_CSR_HPP


#ifndef ATCODER_INTERNAL_QUEUE_HPP
#define ATCODER_INTERNAL_QUEUE_HPP 1

#include <vector>

namespace atcoder {

    namespace internal {

        template <class T> struct simple_queue {
            std::vector<T> payload;
            int pos = 0;
            void reserve(int n) { payload.reserve(n); }
            int size() const { return int(payload.size()) - pos; }
            bool empty() const { return pos == int(payload.size()); }
            void push(const T& t) { payload.push_back(t); }
            T& front() { return payload[pos]; }
            void clear() {
                payload.clear();
                pos = 0;
            }
            void pop() { pos++; }
        };

    }  // namespace internal

}  // namespace atcoder

#endif  // ATCODER_INTERNAL_QUEUE_HPP


#ifndef ATCODER_MINCOSTFLOW_HPP
#define ATCODER_MINCOSTFLOW_HPP 1

#include <algorithm>
#include <cassert>
#include <limits>
#include <queue>
#include <vector>

namespace atcoder {

    template <class Cap, class Cost> struct mcf_graph {
    public:
        mcf_graph() {}
        explicit mcf_graph(int n) : _n(n) {}

        int add_edge(int from, int to, Cap cap, Cost cost) {
            assert(0 <= from && from < _n);
            assert(0 <= to && to < _n);
            assert(0 <= cap);
            assert(0 <= cost);
            int m = int(_edges.size());
            _edges.push_back({ from, to, cap, 0, cost });
            return m;
        }

        struct edge {
            int from, to;
            Cap cap, flow;
            Cost cost;
        };

        edge get_edge(int i) {
            int m = int(_edges.size());
            assert(0 <= i && i < m);
            return _edges[i];
        }
        std::vector<edge> edges() { return _edges; }

        std::pair<Cap, Cost> flow(int s, int t) {
            return flow(s, t, std::numeric_limits<Cap>::max());
        }
        std::pair<Cap, Cost> flow(int s, int t, Cap flow_limit) {
            return slope(s, t, flow_limit).back();
        }
        std::vector<std::pair<Cap, Cost>> slope(int s, int t) {
            return slope(s, t, std::numeric_limits<Cap>::max());
        }
        std::vector<std::pair<Cap, Cost>> slope(int s, int t, Cap flow_limit) {
            assert(0 <= s && s < _n);
            assert(0 <= t && t < _n);
            assert(s != t);

            int m = int(_edges.size());
            std::vector<int> edge_idx(m);

            auto g = [&]() {
                std::vector<int> degree(_n), redge_idx(m);
                std::vector<std::pair<int, _edge>> elist;
                elist.reserve(2 * m);
                for (int i = 0; i < m; i++) {
                    auto e = _edges[i];
                    edge_idx[i] = degree[e.from]++;
                    redge_idx[i] = degree[e.to]++;
                    elist.push_back({ e.from, {e.to, -1, e.cap - e.flow, e.cost} });
                    elist.push_back({ e.to, {e.from, -1, e.flow, -e.cost} });
                }
                auto _g = internal::csr<_edge>(_n, elist);
                for (int i = 0; i < m; i++) {
                    auto e = _edges[i];
                    edge_idx[i] += _g.start[e.from];
                    redge_idx[i] += _g.start[e.to];
                    _g.elist[edge_idx[i]].rev = redge_idx[i];
                    _g.elist[redge_idx[i]].rev = edge_idx[i];
                }
                return _g;
            }();

            auto result = slope(g, s, t, flow_limit);

            for (int i = 0; i < m; i++) {
                auto e = g.elist[edge_idx[i]];
                _edges[i].flow = _edges[i].cap - e.cap;
            }

            return result;
        }

    private:
        int _n;
        std::vector<edge> _edges;

        // inside edge
        struct _edge {
            int to, rev;
            Cap cap;
            Cost cost;
        };

        std::vector<std::pair<Cap, Cost>> slope(internal::csr<_edge>& g,
            int s,
            int t,
            Cap flow_limit) {
            // variants (C = maxcost):
            // -(n-1)C <= dual[s] <= dual[i] <= dual[t] = 0
            // reduced cost (= e.cost + dual[e.from] - dual[e.to]) >= 0 for all edge

            // dual_dist[i] = (dual[i], dist[i])
            std::vector<std::pair<Cost, Cost>> dual_dist(_n);
            std::vector<int> prev_e(_n);
            std::vector<bool> vis(_n);
            struct Q {
                Cost key;
                int to;
                bool operator<(Q r) const { return key > r.key; }
            };
            std::vector<int> que_min;
            std::vector<Q> que;
            auto dual_ref = [&]() {
                for (int i = 0; i < _n; i++) {
                    dual_dist[i].second = std::numeric_limits<Cost>::max();
                }
                std::fill(vis.begin(), vis.end(), false);
                que_min.clear();
                que.clear();

                // que[0..heap_r) was heapified
                size_t heap_r = 0;

                dual_dist[s].second = 0;
                que_min.push_back(s);
                while (!que_min.empty() || !que.empty()) {
                    int v;
                    if (!que_min.empty()) {
                        v = que_min.back();
                        que_min.pop_back();
                    }
                    else {
                        while (heap_r < que.size()) {
                            heap_r++;
                            std::push_heap(que.begin(), que.begin() + heap_r);
                        }
                        v = que.front().to;
                        std::pop_heap(que.begin(), que.end());
                        que.pop_back();
                        heap_r--;
                    }
                    if (vis[v]) continue;
                    vis[v] = true;
                    if (v == t) break;
                    // dist[v] = shortest(s, v) + dual[s] - dual[v]
                    // dist[v] >= 0 (all reduced cost are positive)
                    // dist[v] <= (n-1)C
                    Cost dual_v = dual_dist[v].first, dist_v = dual_dist[v].second;
                    for (int i = g.start[v]; i < g.start[v + 1]; i++) {
                        auto e = g.elist[i];
                        if (!e.cap) continue;
                        // |-dual[e.to] + dual[v]| <= (n-1)C
                        // cost <= C - -(n-1)C + 0 = nC
                        Cost cost = e.cost - dual_dist[e.to].first + dual_v;
                        if (dual_dist[e.to].second - dist_v > cost) {
                            Cost dist_to = dist_v + cost;
                            dual_dist[e.to].second = dist_to;
                            prev_e[e.to] = e.rev;
                            if (dist_to == dist_v) {
                                que_min.push_back(e.to);
                            }
                            else {
                                que.push_back(Q{ dist_to, e.to });
                            }
                        }
                    }
                }
                if (!vis[t]) {
                    return false;
                }

                for (int v = 0; v < _n; v++) {
                    if (!vis[v]) continue;
                    // dual[v] = dual[v] - dist[t] + dist[v]
                    //         = dual[v] - (shortest(s, t) + dual[s] - dual[t]) +
                    //         (shortest(s, v) + dual[s] - dual[v]) = - shortest(s,
                    //         t) + dual[t] + shortest(s, v) = shortest(s, v) -
                    //         shortest(s, t) >= 0 - (n-1)C
                    dual_dist[v].first -= dual_dist[t].second - dual_dist[v].second;
                }
                return true;
            };
            Cap flow = 0;
            Cost cost = 0, prev_cost_per_flow = -1;
            std::vector<std::pair<Cap, Cost>> result = { {Cap(0), Cost(0)} };
            while (flow < flow_limit) {
                if (!dual_ref()) break;
                Cap c = flow_limit - flow;
                for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                    c = std::min(c, g.elist[g.elist[prev_e[v]].rev].cap);
                }
                for (int v = t; v != s; v = g.elist[prev_e[v]].to) {
                    auto& e = g.elist[prev_e[v]];
                    e.cap += c;
                    g.elist[e.rev].cap -= c;
                }
                Cost d = -dual_dist[s].first;
                flow += c;
                cost += c * d;
                if (prev_cost_per_flow == d) {
                    result.pop_back();
                }
                result.push_back({ flow, cost });
                prev_cost_per_flow = d;
            }
            return result;
        }
    };

}  // namespace atcoder

#endif  // ATCODER_MINCOSTFLOW_HPP


using namespace atcoder;


//【最小費用流（負コスト可，負コスト閉路なし）】
/*
* Negative_mcf_graph(int n) : O(1)
*	n 頂点で初期化する．
*
* add_edge(int s, int t, ll cap, ll cost) : O(1)
*	s から t へ容量 cap，コスト cost の辺を追加する．
*
* pll flow(int ST, int GL, ll f_lim = INFL) : O(F (n + m) log n)（F:流量，m:辺の数）
*	ST から GL まで flow_limit まで流せるだけ流したときの {流量, 最小コスト} を返す．
*	制約：負コストの閉路は存在しない
*/
struct Negative_mcf_graph {
	// 参考 : https://ikatakos.com/pot/programming_algorithm/graph_theory/minimum_cost_flow

	// n : 頂点数
	int n;

	// 辺
	struct Edge {
		int to;
		ll cap, cost;

		Edge(int to_, ll cap_, ll cost_) : to(to_), cap(cap_), cost(cost_) {}

#ifdef _MSC_VER
		friend ostream& operator<<(ostream& os, const Edge& e) {
			os << "(to:" << e.to << ", cap:" << e.cap << ", cost:" << e.cost << ")";
			return os;
		}
#endif
	};

	// 元のグラフ（負辺あり）
	vector<vector<Edge>> g;

	// pot[s] : 頂点 s のポテンシャル
	vl pot;

	// n 頂点で初期化する．
	Negative_mcf_graph(int n_) : n(n_), g(n), pot(n, INFL) {}

	// s から t へ容量 cap，コスト cost の辺を追加する．
	void add_edge(int s, int t, ll cap, ll cost) {
		// verify : https://atcoder.jp/contests/code-festival-2014-china-open/tasks/code_festival_china_h

		g[s].emplace_back(t, cap, cost);
	}

	// ベルマンフォード法で ST からの距離を求め，それをポテンシャルとする．
	bool bellman_ford(int ST) {
		pot[ST] = 0;

		rep(i, n) {
			bool updated = false;

			// 全ての辺についての操作
			rep(s, n) repe(e, g[s]) {
				// もし (始点への距離) + (辺のコスト) < (終点への距離) なら (終点への距離) を更新する．
				// INFL からは何を引いても INFL になるようにしているので，ST から到達可能な負閉路しか検出しない．
				if (pot[s] != INFL && pot[s] + e.cost < pot[e.to]) {
					pot[e.to] = pot[s] + e.cost;
					updated = true;
				}
			}

			// もし距離の更新が起こらなければ最短距離確定
			if (!updated) return true;
		}

		// もし全ての辺についての操作を n 回繰り返しても距離の更新があったなら，
		// ST から到達可能な負の閉路を持っているので false を返す．
		return false;
	}

	// ST から GL まで flow_limit まで流せるだけ流したときの {流量, 最小コスト} を返す．
	pll flow(int ST, int GL, ll f_lim = INFL) {
		// verify : https://atcoder.jp/contests/code-festival-2014-china-open/tasks/code_festival_china_h

		// ベルマンフォード法で ST から v までの距離を求め，それをポテンシャル pot[v] とする．
		bool non_neg_cyc = bellman_ford(ST);
		Assert(non_neg_cyc);

		// g_pos : 辺 s→t のコストが (元の辺のコスト) - (pot[t] - pot[s]) >= 0 であるようなグラフ
		mcf_graph<ll, ll> g_pos(n);
		rep(s, n) repe(e, g[s]) {
			g_pos.add_edge(s, e.to, e.cap, e.cost - (pot[e.to] - pot[s]));
		}

		// g_pos の最小費用流を求める．
		ll cap, cost;
		tie(cap, cost) = g_pos.flow(ST, GL, f_lim);

		// 実際のコストは (流量) * pot[GL] を加えたものになる．
		cost += cap * pot[GL];

		return make_pair(cap, cost);
	}
};


ll MLE(int m, int n, int k, vi w, vi a) {
    --a;

    vvi nxt(k + 1, vi(n, k));
    repir(i, k - 1, 0) {
        rep(c, n) {
            nxt[i][c] = nxt[i + 1][c];
        }
        nxt[i][a[i]] = i;
    }
    dumpel(nxt);

    int ST = 2 * k, GL = ST + 1;
    Negative_mcf_graph g(GL + 1);

    rep(i, n) {
        if (nxt[0][i] < k) {
            g.add_edge(ST, 2 * nxt[0][i], 1, w[i]);
        }
        else {
            g.add_edge(ST, GL, 1, 0);
        }
    }

    rep(t, k) {
        g.add_edge(2 * t, 2 * t + 1, 1, -INF);

        rep(i, n) {
            if (a[t] == i) {
                if (nxt[t + 1][i] < k) {
                    g.add_edge(2 * t + 1, 2 * nxt[t + 1][i], 1, 0);
                }
                else {
                    g.add_edge(2 * t + 1, GL, 1, 0);
                }
            }
            else {
                if (nxt[t + 1][i] < k) {
                    g.add_edge(2 * t + 1, 2 * nxt[t + 1][i], 1, w[i]);
                }
            }
        }
    }

    dumpel(g.g);

    ll cap, cost;
    tie(cap, cost) = g.flow(ST, GL, m);
    dump(cap, cost);

    ll res = cost + (ll)INF * k;

    return res;
}


//【自身と同じ数の次の位置】O(n)
/*
* a[0..n) の各要素 a[i] について，j > i かつ a[j] = a[i] となる最小の j を
* nxt[j] に格納する．（存在しなければ n）
*/
template <class T> void next_equal(const vector<T>& a, vi& nxt) {
    // verify : https://atcoder.jp/contests/agc036/tasks/agc036_b

    int n = sz(a);

    // nxt[i] : j > i かつ a[j] = a[i] となる最小の j（なければ n）
    nxt = vi(n, n);

    // num_to_pos[x] : 値 x が最後に現れた位置（右から走査する）
    unordered_map<T, int> num_to_pos;

    repir(i, n - 1, 0) {
        if (num_to_pos.count(a[i])) {
            nxt[i] = num_to_pos[a[i]];
        }
        num_to_pos[a[i]] = i;
    }
}


//【最小費用流（負コスト可，DAG）】
/*
* Negative_mcf_graph(int n) : O(1)
*	n 頂点で初期化する．
*
* add_edge(int s, int t, ll cap, ll cost) : O(1)
*	s から t へ容量 cap，コスト cost の辺を追加する．
*
* pll flow(int ST, int GL, ll f_lim = INFL) : O(F (n + m) log n)（F:流量，m:辺の数）
*	ST から GL まで flow_limit まで流せるだけ流したときの {流量, 最小コスト} を返す．
*	制約：閉路は存在しない
*/
struct Negative_mcf_graph_DAG {
    // 参考 : https://ikatakos.com/pot/programming_algorithm/graph_theory/minimum_cost_flow

    // n : 頂点数
    int n;

    // 辺
    struct Edge {
        int to;
        ll cap, cost;

        Edge(int to_, ll cap_, ll cost_) : to(to_), cap(cap_), cost(cost_) {}

#ifdef _MSC_VER
        friend ostream& operator<<(ostream& os, const Edge& e) {
            os << "(to:" << e.to << ", cap:" << e.cap << ", cost:" << e.cost << ")";
            return os;
        }
#endif
    };

    // 元のグラフ（負辺あり）
    vector<vector<Edge>> g;

    // pot[s] : 頂点 s のポテンシャル
    vl pot;

    // n 頂点で初期化する．
    Negative_mcf_graph_DAG(int n_) : n(n_), g(n), pot(n, INFL) {}

    // s から t へ容量 cap，コスト cost の辺を追加する．
    void add_edge(int s, int t, ll cap, ll cost) {
        // verify : https://atcoder.jp/contests/code-festival-2014-china-open/tasks/code_festival_china_h

        g[s].emplace_back(t, cap, cost);
    }

    // DAG 上の DP で GL までの距離を求め，その -1 倍をポテンシャルとする．
    void DAG_DP(int GL) {
        pot[GL] = 0;

        vb seen(n);
        seen[GL] = true;

        function<ll(int)> dfs = [&](int s) {
            if (seen[s]) return pot[s];
            seen[s] = true;

            repe(e, g[s]) chmin(pot[s], dfs(e.to) + e.cost);

            return pot[s];
        };

        // 各頂点 s についての情報を計算する．
        rep(s, n) if (!seen[s]) dfs(s);

        rep(s, n) pot[s] *= -1;
    }

    // ST から GL まで flow_limit まで流せるだけ流したときの {流量, 最小コスト} を返す．
    pll flow(int ST, int GL, ll f_lim = INFL) {
        // verify : https://atcoder.jp/contests/code-festival-2014-china-open/tasks/code_festival_china_h

        // DAG 上の DP で v から GL までの距離を求め，その -1 倍をポテンシャル pot[v] とする．
        DAG_DP(GL);

        // g_pos : 辺 s→t のコストが (元の辺のコスト) - (pot[t] - pot[s]) >= 0 であるようなグラフ
        mcf_graph<ll, ll> g_pos(n);
        rep(s, n) repe(e, g[s]) {
            g_pos.add_edge(s, e.to, e.cap, e.cost - (pot[e.to] - pot[s]));
        }

        // g_pos の最小費用流を求める．
        ll cap, cost;
        tie(cap, cost) = g_pos.flow(ST, GL, f_lim);

        // 実際のコストは (流量) * (pot[GL] - pot[ST]) を加えたものになる．
        cost += cap * (pot[GL] - pot[ST]);

        return make_pair(cap, cost);
    }
};


int main() {
//	input_from_file("input.txt");
//	output_to_file("output.txt");
	
	int m, n, k;
	cin >> m >> n >> k;

	vi w(n), a(k);
	cin >> w >> a;
    --a;

    ll w_sum = 0;
    rep(i, k) w_sum += w[a[i]];

    vi nxt;
    next_equal(a, nxt);

    Negative_mcf_graph_DAG g(2 * k);

    rep(i, k) {
        g.add_edge(2 * i, 2 * i + 1, 1, -INF);
        g.add_edge(2 * i, 2 * i + 1, m, 0);

        if (i < k - 1) {
            g.add_edge(2 * i + 1, 2 * (i + 1), m, 0);
        }

        if (nxt[i] != k) {
            g.add_edge(2 * i + 1, 2 * nxt[i], 1, -w[a[i]]);
        }
    }
    dumpel(g.g);

    ll cap, cost;
    tie(cap, cost) = g.flow(2 * 0, 2 * (k - 1) + 1, m);
    dump(cap, cost);

    ll res = w_sum + cost + (ll)k * INF;

    cout << res << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 12424
final_rank: 5

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// Minimum Cost Flow (Dijkstra)
// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_6_B&lang=ja
// 蟻本 p. 203
// 始点0, 終点V-1



int V, E, F;
struct edge { int to, cap, cost, rev; };
typedef pair<int, int> P;

vector<edge> G[MAX_N];
int h[MAX_N];
int dist[MAX_N];
int prevv[MAX_N], preve[MAX_N];

// from からtoへ向かう容量cap, コストcostの辺をグラフに追加する
void add_edge(int from, int to, int cap, int cost) {
	edge e1 = { to, cap, cost, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, -cost, G[from].size() - 1 };
	G[to].push_back(e2);

}

// sからtへの流量f最小費用流を求める
// 流せない場合は-1を返す
int min_cost_flow(int s, int t, int f) {
	int res = 0;
	while (f > 0) {
		//ベルマンフォードにより、s-t間最短路を求める
		fill(dist, dist + V, INF);
		dist[s] = 0;
		bool update = true;
		while (update) {
			update = false;
			for (int v = 0; v < V; v++) {
				if (dist[v] == INF) {
					continue;
				}
				for (int i = 0; i < G[v].size(); i++) {
					edge& e = G[v][i];
					if (e.cap > 0 && dist[e.to] > dist[v] + e.cost) {
						dist[e.to] = dist[v] + e.cost;
						prevv[e.to] = v;
						preve[e.to] = i;
						update = true;
					}
				}
			}
		}
		if (dist[t] == INF) {
			return -1;
		}

		// s-t間最短路に沿って目一杯流す。
		int d = f;
		for (int v = t; v != s; v = prevv[v]) {
			d = min(d, G[prevv[v]][preve[v]].cap);
		}

		f -= d;
		res += (d * dist[t]);
		for (int v = t; v != s; v = prevv[v]) {
			edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}

	return res;

}
int M, N, K;
int w[MAX_N];
int a[MAX_N];
int p[MAX_N];


int main() {

	cin >> M >> N >> K;

	for (int i = 0; i < N; i++) {
		cin >> w[i];
	}
	for (int i = 0; i < K; i++) {
		cin >> a[i];
		a[i]--;
	}

	vector<int> b;
	b.push_back(a[0]);

	for (int i = 1; i < K; i++) {
		if (a[i] != b[b.size() - 1]) {
			b.push_back(a[i]);
		}
	}

	V = b.size();

	int S = 0;
	for (int i = 0; i < b.size(); i++) {
		S += w[b[i]];
	}

	for (int i = 0; i < N; i++) {
		p[i] = -1;
	}

	for (int i = 1; i < b.size(); i++) {
		add_edge(i - 1, i, INF, 0);
	}

	for (int i = 0; i < b.size(); i++) {
		if (p[b[i]] == -1) {
			p[b[i]] = i;
			continue;
		}
		
		int id = p[b[i]];
		add_edge(id + 1, i, 1, -w[b[i]]);
		p[b[i]] = i;
	}

	cout << S + min_cost_flow(0, V - 1, M - 1) << endl;


	return 0;
}
accuracy: 1
time_ms: 610
memory_kb: 51092
final_rank: 10

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// Minimum Cost Flow (Dijkstra)
// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_6_B&lang=ja
// 蟻本 p. 203
// 始点0, 終点V-1

int V, E, F;
struct edge { int to, cap, cost, rev; };
typedef pair<int, int> P;

vector<edge> G[MAX_N];
int h[MAX_N];
int dist[MAX_N];
int prevv[MAX_N], preve[MAX_N];

// from からtoへ向かう容量cap, コストcostの辺をグラフに追加する
void add_edge(int from, int to, int cap, int cost) {
	edge e1 = { to, cap, cost, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, -cost, G[from].size() - 1 };
	G[to].push_back(e2);
}

// sからtへの流量f最小費用流を求める
// 流せない場合は-1を返す
int min_cost_flow(int s, int t, int f) {
	int res = 0;
	fill(h, h + V, 0);
	while (f > 0) {
		// ダイクストラ法を用いてhを更新する
		priority_queue<P, vector<P>, greater<P> > que;
		fill(dist, dist + V, INF);
		dist[s] = 0;
		que.push(P(0, s));
		while (!que.empty()) {
			P p = que.top(); que.pop();
			int v = p.second;
			if (dist[v] < p.first) {
				continue;
			}
			for (int i = 0; i < G[v].size(); i++) {
				edge& e = G[v][i];
				if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]) {
					dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
					prevv[e.to] = v;
					preve[e.to] = i;
					que.push(P(dist[e.to], e.to));
				}
			}
		}
		if (dist[t] == INF) {
			return -1;
		}
		for (int v = 0; v < V; v++) {
			h[v] += dist[v];
		}

		// s-t間最短路に沿って目一杯流す
		int d = f;
		for (int v = t; v != s; v = prevv[v]) {
			d = min(d, G[prevv[v]][preve[v]].cap);
		}
		f -= d;
		res += (d * h[t]);
		for (int v = t; v != s; v = prevv[v]) {
			edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}

	return res;

}


int M, N, K;
int w[MAX_N];
int a[MAX_N];
int p[MAX_N];


int main() {

	cin >> M >> N >> K;

	for (int i = 0; i < N; i++) {
		cin >> w[i];
	}
	for (int i = 0; i < K; i++) {
		cin >> a[i];
		a[i]--;
	}

	vector<int> b;
	b.push_back(a[0]);

	for (int i = 1; i < K; i++) {
		if (a[i] != b[b.size() - 1]) {
			b.push_back(a[i]);
		}
	}

	V = b.size();

	int S = 0;
	for (int i = 0; i < b.size(); i++) {
		S += w[b[i]];
	}

	for (int i = 0; i < N; i++) {
		p[i] = -1;
	}

	for (int i = 1; i < b.size(); i++) {
		add_edge(i - 1, i, INF, 0);
	}

	for (int i = 0; i < b.size(); i++) {
		if (p[b[i]] == -1) {
			p[b[i]] = i;
			continue;
		}
		
		int id = p[b[i]];
		add_edge(id + 1, i, 1, -w[b[i]]);
	}

	cout << S + min_cost_flow(0, V - 1, M - 1) << endl;


	return 0;
}
accuracy: 0.13432835820895522
time_ms: 10
memory_kb: 50160
final_rank: 16

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// https://kmyk.github.io/blog/writeups/algo-etc-utpc2011-h/
// http://www.utpc.jp/2011/slides/cache.pdf


int V, E, F;
struct edge { int to, cap, cost, rev; };
typedef pair<int, int> P;

vector<edge> G[MAX_N];
int h[MAX_N];
int dist[MAX_N];
int prevv[MAX_N], preve[MAX_N];

// from からtoへ向かう容量cap, コストcostの辺をグラフに追加する
void add_edge(int from, int to, int cap, int cost) {
	edge e1 = { to, cap, cost, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, -cost, G[from].size() - 1 };
	G[to].push_back(e2);

}

// sからtへの流量f最小費用流を求める
// 流せない場合は-1を返す
int min_cost_flow(int s, int t, int f) {
	int res = 0;
	while (f > 0) {
		//ベルマンフォードにより、s-t間最短路を求める
		fill(dist, dist + V, INF);
		dist[s] = 0;
		bool update = true;
		while (update) {
			update = false;
			for (int v = 0; v < V; v++) {
				if (dist[v] == INF) {
					continue;
				}
				for (int i = 0; i < G[v].size(); i++) {
					edge& e = G[v][i];
					if (e.cap > 0 && dist[e.to] > dist[v] + e.cost) {
						dist[e.to] = dist[v] + e.cost;
						prevv[e.to] = v;
						preve[e.to] = i;
						update = true;
					}
				}
			}
		}
		if (dist[t] == INF) {
			return -1;
		}

		// s-t間最短路に沿って目一杯流す。
		int d = f;
		for (int v = t; v != s; v = prevv[v]) {
			d = min(d, G[prevv[v]][preve[v]].cap);
		}

		f -= d;
		res += (d * dist[t]);
		for (int v = t; v != s; v = prevv[v]) {
			edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}

	return res;

}

int M, N, K;
int w[10010];
vector<int> a;
int p[10010];

int main() {

	cin >> M >> N >> K;


	
	for (int i = 0; i < N; i++) {
		cin >> w[i];
	}
	for (int i = 0; i < K; i++) {
		int aa;
		cin >> aa;
		aa--;
		a.push_back(aa);
	}
	a.erase(unique(a.begin(), a.end()), a.end());
	K = a.size();
	V = K;
	int sum1 = 0;
	for (int i = 0; i < K; i++) {
		sum1 += w[a[i]];
	}

	int s = 0;
	int t = K - 1;

	for (int i = 0; i < K - 1; i++) {
		add_edge(i, i + 1, INF, 0);
	}

	for (int i = 0; i < N; i++) {
		p[i] = -1;
	}

	for (int i = 0; i < K; i++) {
		if (p[a[i]] == -1) {
			p[a[i]] = i;
			continue;
		}

		int b = p[a[i]] + 1;
		int n = i;
		int nw = -w[a[i]];
		add_edge(b, n, 1, nw);
		p[a[i]] = i;

	}

	cout << sum1 + min_cost_flow(s, t, M - 1) << endl;



	return 0;
}
accuracy: 1
time_ms: 620
memory_kb: 51024
final_rank: 11

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// https://kmyk.github.io/blog/writeups/algo-etc-utpc2011-h/
// http://www.utpc.jp/2011/slides/cache.pdf


// Minimum Cost Flow (Dijkstra)
// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_6_B&lang=ja
// 蟻本 p. 203
// 始点0, 終点V-1

int V, E, F;
struct edge { int to, cap, cost, rev; };
typedef pair<int, int> P;

vector<edge> G[MAX_N];
int h[MAX_N];
int dist[MAX_N];
int prevv[MAX_N], preve[MAX_N];

// from からtoへ向かう容量cap, コストcostの辺をグラフに追加する
void add_edge(int from, int to, int cap, int cost) {
	edge e1 = { to, cap, cost, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, -cost, G[from].size() - 1 };
	G[to].push_back(e2);
}

// sからtへの流量f最小費用流を求める
// 流せない場合は-1を返す
int min_cost_flow(int s, int t, int f) {
	int res = 0;
	fill(h, h + V, 0);
	while (f > 0) {
		// ダイクストラ法を用いてhを更新する
		priority_queue<P, vector<P>, greater<P> > que;
		fill(dist, dist + V, INF);
		dist[s] = 0;
		que.push(P(0, s));
		while (!que.empty()) {
			P p = que.top(); que.pop();
			int v = p.second;
			if (dist[v] < p.first) {
				continue;
			}
			for (int i = 0; i < G[v].size(); i++) {
				edge& e = G[v][i];
				if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]) {
					dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
					prevv[e.to] = v;
					preve[e.to] = i;
					que.push(P(dist[e.to], e.to));
				}
			}
		}
		if (dist[t] == INF) {
			return -1;
		}
		for (int v = 0; v < V; v++) {
			h[v] += dist[v];
		}

		// s-t間最短路に沿って目一杯流す
		int d = f;
		for (int v = t; v != s; v = prevv[v]) {
			d = min(d, G[prevv[v]][preve[v]].cap);
		}
		f -= d;
		res += (d * h[t]);
		for (int v = t; v != s; v = prevv[v]) {
			edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}

	return res;

}

int M, N, K;
int w[10010];
vector<int> a;
int p[10010];

int main() {

	cin >> M >> N >> K;


	
	for (int i = 0; i < N; i++) {
		cin >> w[i];
	}
	for (int i = 0; i < K; i++) {
		int aa;
		cin >> aa;
		aa--;
		a.push_back(aa);
	}
	a.erase(unique(a.begin(), a.end()), a.end());
	K = a.size();
	V = K;
	int sum1 = 0;
	for (int i = 0; i < K; i++) {
		sum1 += w[a[i]];
	}

	int s = 0;
	int t = K - 1;

	for (int i = 0; i < K - 1; i++) {
		add_edge(i, i + 1, INF, 0);
	}

	for (int i = 0; i < N; i++) {
		p[i] = -1;
	}

	for (int i = 0; i < K; i++) {
		if (p[a[i]] == -1) {
			p[a[i]] = i;
			continue;
		}

		int b = p[a[i]] + 1;
		int n = i;
		int nw = -w[a[i]];
		add_edge(b, n, 1, nw);

	}

	cout << sum1 + min_cost_flow(s, t, M - 1) << endl;



	return 0;
}
accuracy: 0.13432835820895522
time_ms: 10
memory_kb: 50176
final_rank: 17

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// https://kmyk.github.io/blog/writeups/algo-etc-utpc2011-h/
// http://www.utpc.jp/2011/slides/cache.pdf


// Minimum Cost Flow (Dijkstra)
// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_6_B&lang=ja
// 蟻本 p. 203
// 始点0, 終点V-1

int V, E, F;
struct edge { int to, cap, cost, rev; };
typedef pair<int, int> P;

vector<edge> G[MAX_N];
int h[MAX_N];
int dist[MAX_N];
int prevv[MAX_N], preve[MAX_N];

// from からtoへ向かう容量cap, コストcostの辺をグラフに追加する
void add_edge(int from, int to, int cap, int cost) {
	edge e1 = { to, cap, cost, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, -cost, G[from].size() - 1 };
	G[to].push_back(e2);
}

// sからtへの流量f最小費用流を求める
// 流せない場合は-1を返す
int min_cost_flow(int s, int t, int f) {
	int res = 0;
	fill(h, h + V, 0);
	while (f > 0) {
		// ダイクストラ法を用いてhを更新する
		priority_queue<P, vector<P>, greater<P> > que;
		fill(dist, dist + V, INF);
		dist[s] = 0;
		que.push(P(0, s));
		while (!que.empty()) {
			P p = que.top(); que.pop();
			int v = p.second;
			if (dist[v] < p.first) {
				continue;
			}
			for (int i = 0; i < G[v].size(); i++) {
				edge& e = G[v][i];
				if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]) {
					dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
					prevv[e.to] = v;
					preve[e.to] = i;
					que.push(P(dist[e.to], e.to));
				}
			}
		}
		if (dist[t] == INF) {
			return -1;
		}
		for (int v = 0; v < V; v++) {
			h[v] += dist[v];
		}

		// s-t間最短路に沿って目一杯流す
		int d = f;
		for (int v = t; v != s; v = prevv[v]) {
			d = min(d, G[prevv[v]][preve[v]].cap);
		}
		f -= d;
		res += (d * h[t]);
		for (int v = t; v != s; v = prevv[v]) {
			edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}

	return res;

}

int M, N, K;
int w[10010];
int a[10010];
int p[10010];

int main() {

	cin >> M >> N >> K;


	V = K;

	for (int i = 0; i < N; i++) {
		cin >> w[i];
	}
	int sum1 = 0;
	for (int i = 0; i < K; i++) {
		cin >> a[i];
		a[i]--;
		sum1 += w[a[i]];
	}

	int s = 0;
	int t = K - 1;

	for (int i = 0; i < K - 1; i++) {
		add_edge(i, i + 1, INF, 0);
	}

	for (int i = 0; i < N; i++) {
		p[i] = -1;
	}

	for (int i = 0; i < K; i++) {
		if (p[a[i]] == -1) {
			p[a[i]] = i;
			continue;
		}

		int b = p[a[i]] + 1;
		int n = i;
		int nw = -w[a[i]];
		add_edge(b, n, 1, nw);

	}

	cout << sum1 + min_cost_flow(s, t, M - 1) << endl;



	return 0;
}
accuracy: 0.04477611940298507
time_ms: 10
memory_kb: 50004
final_rank: 18

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// https://kmyk.github.io/blog/writeups/algo-etc-utpc2011-h/
// http://www.utpc.jp/2011/slides/cache.pdf


// Minimum Cost Flow (Dijkstra)
// https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_6_B&lang=ja
// 蟻本 p. 203
// 始点0, 終点V-1

int V, E, F;
struct edge { int to, cap, cost, rev; };
typedef pair<int, int> P;

vector<edge> G[MAX_N];
int h[MAX_N];
int dist[MAX_N];
int prevv[MAX_N], preve[MAX_N];

// from からtoへ向かう容量cap, コストcostの辺をグラフに追加する
void add_edge(int from, int to, int cap, int cost) {
	edge e1 = { to, cap, cost, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, -cost, G[from].size() - 1 };
	G[to].push_back(e2);
}

// sからtへの流量f最小費用流を求める
// 流せない場合は-1を返す
int min_cost_flow(int s, int t, int f) {
	int res = 0;
	fill(h, h + V, 0);
	while (f > 0) {
		// ダイクストラ法を用いてhを更新する
		priority_queue<P, vector<P>, greater<P> > que;
		fill(dist, dist + V, INF);
		dist[s] = 0;
		que.push(P(0, s));
		while (!que.empty()) {
			P p = que.top(); que.pop();
			int v = p.second;
			if (dist[v] < p.first) {
				continue;
			}
			for (int i = 0; i < G[v].size(); i++) {
				edge& e = G[v][i];
				if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]) {
					dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
					prevv[e.to] = v;
					preve[e.to] = i;
					que.push(P(dist[e.to], e.to));
				}
			}
		}
		if (dist[t] == INF) {
			return -1;
		}
		for (int v = 0; v < V; v++) {
			h[v] += dist[v];
		}

		// s-t間最短路に沿って目一杯流す
		int d = f;
		for (int v = t; v != s; v = prevv[v]) {
			d = min(d, G[prevv[v]][preve[v]].cap);
		}
		f -= d;
		res += (d * h[t]);
		for (int v = t; v != s; v = prevv[v]) {
			edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}

	return res;

}

int M, N, K;
int w[1010];
int a[1010];
int p[1010];

int main() {

	cin >> M >> N >> K;


	V = K;

	for (int i = 0; i < N; i++) {
		cin >> w[i];
	}
	int sum1 = 0;
	for (int i = 0; i < K; i++) {
		cin >> a[i];
		a[i]--;
		sum1 += w[a[i]];
	}

	int s = 0;
	int t = K - 1;

	for (int i = 0; i < K - 1; i++) {
		add_edge(i, i + 1, INF, 0);
	}

	for (int i = 0; i < N; i++) {
		p[i] = -1;
	}

	for (int i = 0; i < K; i++) {
		if (p[a[i]] == -1) {
			p[a[i]] = i;
			continue;
		}

		int b = p[a[i]] + 1;
		int n = i;
		int nw = -w[a[i]];
		add_edge(b, n, 1, nw);

	}

	cout << sum1 + min_cost_flow(s, t, M - 1) << endl;



	return 0;
}
accuracy: 0.04477611940298507
time_ms: 10
memory_kb: 50084
final_rank: 19

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// https://kmyk.github.io/blog/writeups/algo-etc-utpc2011-h/
// http://www.utpc.jp/2011/slides/cache.pdf

int V, E, F;
struct edge { int to, cap, cost, rev; };

vector<edge> G[MAX_N];
int dist[MAX_N];
int prevv[MAX_N], preve[MAX_N];

// from からtoへ向かう容量cap, コストcostの辺をグラフに追加する
void add_edge(int from, int to, int cap, int cost) {
	edge e1 = { to, cap, cost, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, -cost, G[from].size() - 1 };
	G[to].push_back(e2);

}

// sからtへの流量f最小費用流を求める
// 流せない場合は-1を返す
int min_cost_flow(int s, int t, int f) {
	int res = 0;
	while (f > 0) {
		//ベルマンフォードにより、s-t間最短路を求める
		fill(dist, dist + V, INF);
		dist[s] = 0;
		bool update = true;
		while (update) {
			update = false;
			for (int v = 0; v < V; v++) {
				if (dist[v] == INF) {
					continue;
				}
				for (int i = 0; i < G[v].size(); i++) {
					edge& e = G[v][i];
					if (e.cap > 0 && dist[e.to] > dist[v] + e.cost) {
						dist[e.to] = dist[v] + e.cost;
						prevv[e.to] = v;
						preve[e.to] = i;
						update = true;
					}
				}
			}
		}
		if (dist[t] == INF) {
			return -1;
		}

		// s-t間最短路に沿って目一杯流す。
		int d = f;
		for (int v = t; v != s; v = prevv[v]) {
			d = min(d, G[prevv[v]][preve[v]].cap);
		}

		f -= d;
		res += (d * dist[t]);
		for (int v = t; v != s; v = prevv[v]) {
			edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}

	return res;

}

int N, M, K;
int w[MAX_N];
int last[MAX_N];

int main() {


	cin >> M >> N >> K;
	for (int i = 0; i < N; i++) {
		cin >> w[i];
	}
	vector<int> a(K);
	for (int i = 0; i < K; i++) {
		cin >> a[i];
		a[i]--;
	}

	a.erase(unique(a.begin(), a.end()), a.end());
	K = a.size();
	V = K;
	int s = 0;
	int t = V - 1;
	int base = 0;
	for (int i = 0; i < K; i++) {
		base += w[a[i]];
	}

	for (int i = 0; i < K - 1; i++) {
		add_edge(i, i + 1, INF, 0);
	}

	memset(last, -1, sizeof(last));

	for (int i = 0; i < K; i++) {
		int idx = a[i];
		if (last[idx] == -1) {
			last[idx] = i;
			continue;
		}

		add_edge(last[idx] + 1, i, 1, -w[idx]);
		last[idx] = i;
	}

	int ans = base + min_cost_flow(s, t, M - 1);

	cout << ans << endl;

	
	return 0;
}
accuracy: 1
time_ms: 620
memory_kb: 58808
final_rank: 15

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// http://www.utpc.jp/2011/slides/cache.pdf

int V, E, F;
struct edge { int to, cap, cost, rev; };

vector<edge> G[MAX_N];
int dist[MAX_N];
int prevv[MAX_N], preve[MAX_N];

// from からtoへ向かう容量cap, コストcostの辺をグラフに追加する
void add_edge(int from, int to, int cap, int cost) {
	edge e1 = { to, cap, cost, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, -cost, G[from].size() - 1 };
	G[to].push_back(e2);

}

// sからtへの流量f最小費用流を求める
// 流せない場合は-1を返す
int min_cost_flow(int s, int t, int f) {
	int res = 0;
	while (f > 0) {
		//ベルマンフォードにより、s-t間最短路を求める
		fill(dist, dist + V, INF);
		dist[s] = 0;
		bool update = true;
		while (update) {
			update = false;
			for (int v = 0; v < V; v++) {
				if (dist[v] == INF) {
					continue;
				}
				for (int i = 0; i < G[v].size(); i++) {
					edge& e = G[v][i];
					if (e.cap > 0 && dist[e.to] > dist[v] + e.cost) {
						dist[e.to] = dist[v] + e.cost;
						prevv[e.to] = v;
						preve[e.to] = i;
						update = true;
					}
				}
			}
		}
		if (dist[t] == INF) {
			return -1;
		}

		// s-t間最短路に沿って目一杯流す。
		int d = f;
		for (int v = t; v != s; v = prevv[v]) {
			d = min(d, G[prevv[v]][preve[v]].cap);
		}

		f -= d;
		res += (d * dist[t]);
		for (int v = t; v != s; v = prevv[v]) {
			edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}

	return res;

}

int N, M, K;
int w[MAX_N];
int last[MAX_N];

int main() {


	cin >> M >> N >> K;
	for (int i = 0; i < N; i++) {
		cin >> w[i];
	}
	vector<int> a(K);
	for (int i = 0; i < K; i++) {
		cin >> a[i];
		a[i]--;
	}

	a.erase(unique(a.begin(), a.end()), a.end());
	K = a.size();
	V = K;
	int s = 0;
	int t = V - 1;
	int base = 0;
	for (int i = 0; i < K; i++) {
		base += w[a[i]];
	}

	for (int i = 0; i < K - 1; i++) {
		add_edge(i, i + 1, INF, 0);
	}

	memset(last, -1, sizeof(last));

	for (int i = 0; i < K; i++) {
		int idx = a[i];
		if (last[idx] == -1) {
			last[idx] = i;
			continue;
		}

		add_edge(last[idx] + 1, i, 1, -w[idx]);
		last[idx] = i;
	}

	int ans = base + min_cost_flow(s, t, M - 1);

	cout << ans << endl;

	
	return 0;
}
accuracy: 1
time_ms: 610
memory_kb: 58864
final_rank: 14

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// https://area.hateblo.jp/entry/2017/12/18/010657

int V, E, F;
struct edge { int to, cap, cost, rev; };

vector<edge> G[MAX_N];
int dist[MAX_N];
int prevv[MAX_N], preve[MAX_N];

// from からtoへ向かう容量cap, コストcostの辺をグラフに追加する
void add_edge(int from, int to, int cap, int cost) {
	edge e1 = { to, cap, cost, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, -cost, G[from].size() - 1 };
	G[to].push_back(e2);

}

// sからtへの流量f最小費用流を求める
// 流せない場合は-1を返す
int min_cost_flow(int s, int t, int f) {
	int res = 0;
	while (f > 0) {
		//ベルマンフォードにより、s-t間最短路を求める
		fill(dist, dist + V, INF);
		dist[s] = 0;
		bool update = true;
		while (update) {
			update = false;
			for (int v = 0; v < V; v++) {
				if (dist[v] == INF) {
					continue;
				}
				for (int i = 0; i < G[v].size(); i++) {
					edge& e = G[v][i];
					if (e.cap > 0 && dist[e.to] > dist[v] + e.cost) {
						dist[e.to] = dist[v] + e.cost;
						prevv[e.to] = v;
						preve[e.to] = i;
						update = true;
					}
				}
			}
		}
		if (dist[t] == INF) {
			return -1;
		}

		// s-t間最短路に沿って目一杯流す。
		int d = f;
		for (int v = t; v != s; v = prevv[v]) {
			d = min(d, G[prevv[v]][preve[v]].cap);
		}

		f -= d;
		res += (d * dist[t]);
		for (int v = t; v != s; v = prevv[v]) {
			edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}

	return res;

}

int N, M, K;
int w[MAX_N];
int last[MAX_N];

int main() {


	cin >> M >> N >> K;
	for (int i = 0; i < N; i++) {
		cin >> w[i];
	}
	vector<int> a(K);
	for (int i = 0; i < K; i++) {
		cin >> a[i];
		a[i]--;
	}

	a.erase(unique(a.begin(), a.end()), a.end());
	K = a.size();
	V = K;
	int s = 0;
	int t = V - 1;
	int base = 0;
	for (int i = 0; i < K; i++) {
		base += w[a[i]];
	}

	for (int i = 0; i < K - 1; i++) {
		add_edge(i, i + 1, INF, 0);
	}

	memset(last, -1, sizeof(last));

	for (int i = 0; i < K; i++) {
		int idx = a[i];
		if (last[idx] == -1) {
			last[idx] = i;
			continue;
		}

		add_edge(last[idx] + 1, i, 1, -w[idx]);
		last[idx] = i;
	}

	int ans = base + min_cost_flow(s, t, M - 1);

	cout << ans << endl;

	
	return 0;
}
accuracy: 1
time_ms: 610
memory_kb: 58808
final_rank: 13

code_snippet: #define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include<sstream>
#include<list>
#include<iomanip>
#include <cstdlib>
#include <cstring>
#include <stack>
#include <bitset>
#include <cassert>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 100000000;
const long long LINF = 3e18 + 7;
const int MAX_N = 2000010;
const int MAX_W = 10002;
const int MAX_ARRAYK = 100000;
double PI = 3.14159265358979323846;
//using ll = long long;

// https://area.hateblo.jp/entry/2017/12/18/010657

int V, E, F;
struct edge { int to, cap, cost, rev; };

vector<edge> G[MAX_N];
int dist[MAX_N];
int prevv[MAX_N], preve[MAX_N];

// from からtoへ向かう容量cap, コストcostの辺をグラフに追加する
void add_edge(int from, int to, int cap, int cost) {
	edge e1 = { to, cap, cost, G[to].size() };
	G[from].push_back(e1);
	edge e2 = { from, 0, -cost, G[from].size() - 1 };
	G[to].push_back(e2);

}

// sからtへの流量f最小費用流を求める
// 流せない場合は-1を返す
int min_cost_flow(int s, int t, int f) {
	int res = 0;
	while (f > 0) {
		//ベルマンフォードにより、s-t間最短路を求める
		fill(dist, dist + V, INF);
		dist[s] = 0;
		bool update = true;
		while (update) {
			update = false;
			for (int v = 0; v < V; v++) {
				if (dist[v] == INF) {
					continue;
				}
				for (int i = 0; i < G[v].size(); i++) {
					edge& e = G[v][i];
					if (e.cap > 0 && dist[e.to] > dist[v] + e.cost) {
						dist[e.to] = dist[v] + e.cost;
						prevv[e.to] = v;
						preve[e.to] = i;
						update = true;
					}
				}
			}
		}
		if (dist[t] == INF) {
			return -1;
		}

		// s-t間最短路に沿って目一杯流す。
		int d = f;
		for (int v = t; v != s; v = prevv[v]) {
			d = min(d, G[prevv[v]][preve[v]].cap);
		}

		f -= d;
		res += (d * dist[t]);
		for (int v = t; v != s; v = prevv[v]) {
			edge& e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}

	return res;

}

int N, M, K;
int w[MAX_N];
int a[MAX_N];
int last[MAX_N];

int main() {


	cin >> M >> N >> K;
	for (int i = 0; i < N; i++) {
		cin >> w[i];
	}

	for (int i = 0; i < K; i++) {
		cin >> a[i];
		a[i]--;
	}

	V = K;
	int s = 0;
	int t = V - 1;
	int base = 0;
	for (int i = 0; i < K; i++) {
		base += w[a[i]];
	}

	for (int i = 0; i < K - 1; i++) {
		add_edge(i, i + 1, INF, 0);
	}

	memset(last, -1, sizeof(last));

	for (int i = 0; i < K; i++) {
		int idx = a[i];
		if (last[idx] == -1) {
			last[idx] = i;
			continue;
		}

		add_edge(last[idx] + 1, i, 1, -w[idx]);
		last[idx] = i;
	}

	int ans = base + min_cost_flow(s, t, M - 1);

	cout << ans << endl;

	
	return 0;
}
accuracy: 0.04477611940298507
time_ms: 10
memory_kb: 57848
final_rank: 20

code_snippet: #include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;

const int maxn=10000+50;
const int maxv=maxn;
const int INF=100000000;


typedef pair<int,int>P;

struct edge{
    int to,cap,cost,rev;
    edge(){}
    edge(int t,int ca,int co,int r):to(t),cap(ca),cost(co),rev(r){}
};

int V;
vector<edge>es[maxv];

int h[maxv];
int dist[maxv];
int prevv[maxv],preve[maxv];

void add_edge(int f,int t,int ca,int co){
    es[f].push_back(edge(t,ca,co,es[t].size()));
    es[t].push_back(edge(f,0,-co,es[f].size()-1));
}

int min_cost_flow(int s,int t,int f){
    fill(h,h+V,INF);
    h[s]=0;

    int res=0;

    bool update=true;
    while(update){
        update=false;

        for(int v=0;v<V;v++){
            if(h[v]==INF)continue;

            for(int i=0;i<es[v].size();i++){
                edge& e=es[v][i];
                if(e.cap>0&&h[e.to]>h[v]+e.cost){
                    h[e.to]=h[v]+e.cost;
                    prevv[e.to]=v;
                    preve[e.to]=i;
                    update=true;
                }
            }
        }
    }

    if(h[t]==INF)return -1;

    int d=f;
    for(int v=t;v!=s;v=prevv[v])
        d=min(d,es[prevv[v]][preve[v]].cap);

    f-=d;
    res+=d*h[t];

    for(int v=t;v!=s;v=prevv[v]){
        edge& e=es[prevv[v]][preve[v]];
        e.cap-=d;
        es[v][e.rev].cap+=d;
    }

    while(f>0){
        fill(dist,dist+V,INF);
        dist[s]=0;

        priority_queue<P,vector<P>,greater<P> >que;
        que.push(P(0,s));

        while(!que.empty()){
            int v=que.top().second;que.pop();

            for(int i=0;i<es[v].size();i++){
                edge&e=es[v][i];

                if(e.cap>0&&dist[e.to]>dist[v]+e.cost+h[v]-h[e.to]){
                    dist[e.to]=dist[v]+e.cost+h[v]-h[e.to];
                    prevv[e.to]=v;
                    preve[e.to]=i;
                    que.push(P(dist[e.to],e.to));
                }
            }
        }

        if(dist[t]==INF)return -1;

        for(int i=0;i<V;i++)h[i]+=dist[i];

        int d=f;
        for(int v=t;v!=s;v=prevv[v])
            d=min(d,es[prevv[v]][preve[v]].cap);

        f-=d;
        res+=d*h[t];

        for(int v=t;v!=s;v=prevv[v]){
            edge& e=es[prevv[v]][preve[v]];
            e.cap-=d;
            es[v][e.rev].cap+=d;
        }
    }
    return res;
}

int M,N,K;
int w[maxn];
int a[maxn];

int reco[maxn];

void solve(){
    scanf("%d%d%d",&M,&N,&K);

    for(int i=1;i<=N;i++)
        scanf("%d",&w[i]);

    int ans=0;
    int n=0;

    for(int i=0;i<K;i++){
        int u;
        scanf("%d",&u);
        if(n==0||u!=a[n-1]){
            a[n++]=u;
            ans+=w[u];
        }
    }

    V=n;

    for(int i=0;i<maxn;i++)es[i].clear();

    memset(reco,-1,sizeof(reco));

    for(int i=0;i<n;i++){
        int u=a[i];
        if(reco[u]>=0)
            add_edge(reco[u],i-1,1,-w[u]);
        reco[u]=i;

        add_edge(i,i+1,INF,0);
    }

    printf("%d\n",ans+min_cost_flow(0,n-1,M-1));
}
int main()
{
    solve();
}
accuracy: 1
time_ms: 10
memory_kb: 4088
final_rank: 1

code_snippet: #include<bits/stdc++.h>
using namespace std;
const int INF =1e9;
const int inf = 2e4;
const int max_v = 2e4 + 10;
typedef pair<int,int> P;
struct edge{
	int to,cap,cost,rev;
	edge(int _to,int _cap,int _cost,int _rev) : to(_to),cap(_cap),cost(_cost),rev(_rev){}
};
vector<edge> g[max_v]; 
void add_edge(int from,int to,int cap,int cost){
	g[from].push_back(edge(to,cap,cost,g[to].size()));
	g[to].push_back(edge(from,0,-1*cost,g[from].size() - 1));
}

//两次变换：
//1：顶点流量限制，加上 -inf；保证最小费用高于-2e9; 
//2：加势去负边  ，点加上(V - 点号) * inf; 保证加势后低于2e9;

//算出来后：
//1：先去势得到最小费用 
//2：去流量限制后的真确结果；
//3：总费用加上节省副费用得到结果；

//对于点v:
// 入点为2*v,出点为2*v + 1; 

int box,ball,n,V;

int dist[max_v],h[max_v];
int prevv[max_v],preve[max_v];

//n为点数 
//V为改造图总点数

int min_cost_flow(int s,int t,int f){
	int ret = 0;
	fill(h,h+V,0);
	while(f > 0){
		fill(dist,dist + V,INF);
		priority_queue<P,vector<P>,greater<P> > que;
		dist[s] = 0;
		que.push(P(dist[s],s));
		int letmesee = 0;
		while(!que.empty()){
			P p = que.top();que.pop();
			int v = p.second;
			if(dist[v] < p.first)continue;
			for(int i = 0;i < g[v].size();i++){
				edge &e = g[v][i];
				if(e.cap > 0 && dist[v] + e.cost + h[v] - h[e.to] < dist[e.to]){
					dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
					que.push(P(dist[e.to],e.to));
					prevv[e.to] = v;
					preve[e.to] = i;
				}
			}
		}
		if(dist[t] == INF)return -1;
		for(int i = 0;i < V;i++)h[i] += dist[i];
		int d = f;
		for(int v = t;v != s;v = prevv[v]){
			edge &e = g[prevv[v]][preve[v]];
			d = min(d,e.cap);
		}
		f -= d;
		ret += h[t] * d;
		//还原势 
		ret -= inf * (t - s) * d;
		for(int v = t;v != s;v = prevv[v]){
			edge &e = g[prevv[v]][preve[v]];
			e.cap -= d;
			g[v][e.rev].cap += d;
		}
	}
	//还原点限制 
	ret += n * inf;
	return ret;
}


vector<int> es[max_v];

int vin(int v){
	return 2 * v;
}
int vout(int v){
	return 2 * v + 1;
}

int main(){
	int ans = 0;
	int ballcost[max_v];
	int order[max_v];
	scanf("%d%d%d",&box,&ball,&n);
	for(int i = 0;i < ball;i++){
		scanf("%d",&ballcost[i]);
	}
	for(int v = 0;v < n;v++){
		scanf("%d",&order[v]);
		order[v]--;
		ans += ballcost[order[v]];
		es[order[v]].push_back(v);
		add_edge(vin(v),vout(v),1,-1*inf + inf);
		add_edge(vin(v),vout(v),INF,0 + inf);
		if(v + 1 < n)add_edge(vout(v),vin(v+1),INF,0 + inf * (vin(v+1) - vout(v)));
	}
	for(int i = 0;i < ball;i++){
		int kkk = es[i].size();
		for(int j = 0;j < kkk - 1;j++){
			int v = es[i][j];
			int u = es[i][j + 1];
			add_edge(vout(v),vin(u),1,-1 * ballcost[i] + inf * (vin(u) - vout(v)));
		} 
	}
	V = 2 * n;
	cout << ans + min_cost_flow(vin(0),vout(n-1),box) << endl;
	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 6408
final_rank: 2

code_snippet: #include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#define min(a,b)    (((a) < (b)) ? (a) : (b))
#define max(a,b)    (((a) > (b)) ? (a) : (b))
#define abs(x)    ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define delta 0.85
#define eps 1e-10
#define PI 3.14159265358979323846
using namespace std;

#define MAX_V 10005
struct edge{
	int to, cap, cost, rev;
	edge(int to, int cap, int cost, int rev):to(to), cap(cap), cost(cost), rev(rev){}
};

int V;
vector<edge> G[MAX_V];
int h[MAX_V], dist[MAX_V];
int prevv[MAX_V], preve[MAX_V];
bool inq[MAX_V];

void add_edge(int from, int to, int cap, int cost){
	G[from].push_back(edge(to, cap, cost, G[to].size()));
	G[to].push_back(edge(from, 0, -cost, G[from].size() - 1));
}

int min_cost_flow(int s, int t, int f){
	int res = 0;
	memset(h, 0, sizeof(h));
	// Spfa 处理负权边
	memset(inq, 0, sizeof(inq));
	memset(dist, 0x3f, sizeof(dist));
	queue<int> q;
	dist[s] = 0;
	q.push(s);
	inq[s] = 1;
	while(!q.empty()){
		int v = q.front(); q.pop();
		inq[v] = 0;
		for(int i = 0; i < G[v].size(); i++){
			edge &e = G[v][i];
			int d2 = dist[v] + e.cost + h[v] - h[e.to];
			if(e.cap > 0 && dist[e.to] > d2){
				dist[e.to] = d2;
				if(!inq[e.to]){
					q.push(e.to);
					inq[e.to] = 1;
				}
			}
		}
	}
	for(int v = 0; v < V; v++) h[v] += dist[v];
	
	while(f > 0){
		// Dijkstra 连续最短路求最小费用流
		priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que;
		memset(dist, 0x3f, sizeof(dist));
		dist[s] = 0;
		que.push(pair<int, int>(0, s));
		while(!que.empty()){
			pair<int, int> p = que.top(); que.pop();
			int v = p.second;
			if(dist[v] < p.first) continue;
			for(int i = 0; i < G[v].size(); i++){
				edge &e = G[v][i];
				int d2 = dist[v] + e.cost + h[v] - h[e.to];
				if(e.cap > 0 && d2 < dist[e.to]){
					dist[e.to] = d2;
					prevv[e.to] = v;
					preve[e.to] = i;
					que.push(pair<int, int>(dist[e.to], e.to));
				}
			}
		}
		if(dist[t] == INF){
			return -1;
		}
		for(int v = 0; v < V; v++) h[v] += dist[v];
		int d = f;
		for(int v = t; v != s; v = prevv[v]){
			d = min(d, G[prevv[v]][preve[v]].cap);
		}
		f -= d;
		res += d * h[t];
		for(int v = t; v != s; v = prevv[v]){
			edge &e = G[prevv[v]][preve[v]];
			e.cap -= d;
			G[v][e.rev].cap += d;
		}
	}
	return res;
}

void clear_graph(){
	for(int v = 0; v < V; v++) G[v].clear();
}

#define MAX_K 10000
#define MAX_N 10000
int M, N, K;
int w[MAX_N];
int a[MAX_K], pre[MAX_K];

void solve(){
	int res = 0;
	memset(pre, -1, sizeof(pre));
	// 连续区间相同元素去重
	int na = unique(a, a + K) - a;
	// 初始化图
	int s = na, t = s + 1;
	V = t + 1;
	clear_graph();
	// 建图
	add_edge(s, 0, M - 1, 0);
	add_edge(na - 1, t, M - 1, 0);
	for(int i = 0; i < na - 1; i++){
		add_edge(i, i + 1, INF, 0);
	}
	int cnt = 0;
	for(int i = 0; i < na; i++){
		if(pre[a[i]] != -1){
			add_edge(pre[a[i]] + 1, i, 1, -w[a[i]]);
		}
		pre[a[i]] = i;
	}
	// 答案为最坏情况费用加上最小费用流结果
	for(int i = 0; i < na; i++) res += w[a[i]];
	res += min_cost_flow(s, t, M - 1);
	printf("%d\n", res);
}

int main(){
	while(~scanf("%d%d%d", &M, &N, &K)){
		for(int i = 0; i < N; i++){
			scanf("%d", w + i);
		}
		for(int i = 0; i < K; i++){
			scanf("%d", a + i);
			--a[i];
		}
		solve();
	}
	return 0;
}
accuracy: 1
time_ms: 260
memory_kb: 4700
final_rank: 6

code_snippet: #include<bits/stdc++.h>
using namespace std;
const int INF =1e9;
const int inf = 2e4;
const int max_v = 2e4 + 10;
typedef pair<int,int> P;
struct edge{
	int to,cap,cost,rev;
	edge(int _to,int _cap,int _cost,int _rev) : to(_to),cap(_cap),cost(_cost),rev(_rev){}
};
vector<edge> g[max_v]; 
void add_edge(int from,int to,int cap,int cost){
	g[from].push_back(edge(to,cap,cost,g[to].size()));
	g[to].push_back(edge(from,0,-1*cost,g[from].size() - 1));
}

//两次变换：
//1：顶点流量限制，加上 -inf；保证最小费用高于-2e9; 
//2：加势去负边  ，点加上(V - 点号) * inf; 保证加势后低于2e9;

//算出来后：
//1：先去势得到最小费用 
//2：去流量限制后的真确结果；
//3：总费用加上节省副费用得到结果；

//对于点v:
// 入点为2*v,出点为2*v + 1; 

int box,ball,n,V;

int dist[max_v],h[max_v];
int prevv[max_v],preve[max_v];

//n为点数 
//V为改造图总点数

int min_cost_flow(int s,int t,int f){
	int ret = 0;
	fill(h,h+V,0);
	while(f > 0){
		fill(dist,dist + V,INF);
		priority_queue<P,vector<P>,greater<P> > que;
		dist[s] = 0;
		que.push(P(dist[s],s));
		int letmesee = 0;
		while(!que.empty()){
			P p = que.top();que.pop();
			int v = p.second;
			if(dist[v] < p.first)continue;
			for(int i = 0;i < g[v].size();i++){
				edge &e = g[v][i];
				if(e.cap > 0 && dist[v] + e.cost + h[v] - h[e.to] < dist[e.to]){
					dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
					que.push(P(dist[e.to],e.to));
					prevv[e.to] = v;
					preve[e.to] = i;
				}
			}
		}
		if(dist[t] == INF)return -1;
		for(int i = 0;i < V;i++)h[i] += dist[i];
		int d = f;
		for(int v = t;v != s;v = prevv[v]){
			edge &e = g[prevv[v]][preve[v]];
			d = min(d,e.cap);
		}
		f -= d;
		ret += h[t] * d;
		//还原势 
		ret -= inf * (t - s) * d;
		for(int v = t;v != s;v = prevv[v]){
			edge &e = g[prevv[v]][preve[v]];
			e.cap -= d;
			g[v][e.rev].cap += d;
		}
	}
	//还原点限制 
	ret += n * inf;
	return ret;
}


vector<int> es[max_v];

int vin(int v){
	return 2 * v;
}
int vout(int v){
	return 2 * v + 1;
}

int main(){
	int ans = 0;
	int ballcost[max_v];
	int order[max_v];
	scanf("%d%d%d",&box,&ball,&n);
	for(int i = 0;i < ball;i++){
		scanf("%d",&ballcost[i]);
	}
	for(int v = 0;v < n;v++){
		scanf("%d",&order[v]);
		order[v]--;
		ans += ballcost[order[v]];
		es[order[v]].push_back(v);
		add_edge(vin(v),vout(v),1,-1*inf + inf);
		add_edge(vin(v),vout(v),INF,0 + inf);
		if(v + 1 < n)add_edge(vout(v),vin(v+1),INF,0 + inf * (vin(v+1) - vout(v)));
	}
	for(int i = 0;i < ball;i++){
		int kkk = es[i].size();
		for(int j = 0;j < kkk - 1;j++){
			int v = es[i][j];
			int u = es[i][j + 1];
			add_edge(vout(v),vin(u),1,-1 * ballcost[i] + inf * (vin(u) - vout(v)));
		} 
	}
	V = 2 * n;
//	printf("V = %d\n",V);
//	for(int i = 0;i < V;i++){
//		for(int j = 0;j < g[i].size();j++){
//			edge &e = g[i][j];
//			if(e.cap > 0){
//				if(e.to >= V)cout << "越界"<<endl;
//				if(e.cost < 0)cout << "负边"<<endl;
//				if(e.to <= i)cout << "指回去了"<<endl;
//			}
//		}
//	}
//	cout << "未出现上述情况"<<endl;
	cout << ans + min_cost_flow(vin(0),vout(n-1),box) << endl;
	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 6444
final_rank: 3