#include using namespace std; #define ALL(x) (x).begin(),(x).end() #define REP(i, n) for(ll i=0; i<(ll)(n); i++) #define PER(i, n) for(ll i=(ll)(n)-1; i>=0; i--) template int LB(const vector& v, T x) { return lower_bound(ALL(v),x)-v.begin(); } template int UQ(T& v) { sort(ALL(v)); v.erase(unique(ALL(v)),v.end()); return v.size(); } template bool chmax(T &a, T b) { return a bool chmin(T &a, T b) { return a>b ? a=b, true : false; } template using rpriority_queue = priority_queue,greater>; using ll=long long; const int INF=1e9+10; const ll INFL=4e18; using ld=long double; using lll=__int128_t; using ull=unsigned long long; using VI=vector; using VVI=vector; using VL=vector; using VVL=vector; using PL=pair; using VP=vector; using WG=vector>>; #ifdef LOCAL #include "./debug.hpp" #else #define debug(...) #define print_line #endif /// @brief 最大流 struct MaxFlow { /// @brief 辺構造体 struct Edge { int from; ///< 始点 int to; ///< 終点 int rev; ///< 逆辺のインデックス ll cap; ///< 容量 ll flow; ///< 流量 bool isrev; Edge(int from, int to, ll cap, int rev, bool isrev): from(from),to(to),rev(rev),cap(cap),flow(0),isrev(isrev) {} }; MaxFlow(int n): graph(n), level(n), iter(n) {} MaxFlow()=default; /// @brief 容量 cap の辺を追加する void add_edge(int from, int to, ll cap) { graph[from].push_back(Edge(from,to,cap,graph[to].size(),false)); graph[to].push_back(Edge(to,from,0,graph[from].size()-1,true)); } private: vector> graph; vector level,iter; void bfs(int s) { fill(level.begin(),level.end(),-1); queue que; level[s]=0; que.push(s); while(!que.empty()) { int v=que.front(); que.pop(); for(auto &e:graph[v]) { if(e.cap>0&&level[e.to]<0) { level[e.to]=level[v]+1; que.push(e.to); } } } } ll dfs(int v, int t, ll f) { if(v==t) return f; for(int& i=iter[v]; i<(int)graph[v].size(); i++) { auto& e=graph[v][i]; if(e.cap>0&&level[v]0) { e.cap-=d,graph[e.to][e.rev].cap+=d; e.flow+=d,graph[e.to][e.rev].flow-=d; return d; } } } return 0; } public: /// @brief s から t への最大流を求める /// @note O(V^2 E) ll flow(int s, int t) { ll ret=0; while(true) { bfs(s); if(level[t]==-1) return ret; fill(iter.begin(),iter.end(),0); ll f; while((f=dfs(s,t,INFL))>0) ret+=f; } } /// @brief 直前に流したフローから最小カットを復元する /// @brief 始点 v から到達可能か否か vector mincut(int v=0) { vector ret(graph.size()); queue que; que.push(v); ret[v]=true; while(!que.empty()) { int v=que.front(); que.pop(); for(auto& e:graph[v]) { if(e.cap>0&&!ret[e.to]) { ret[e.to]=true; que.push(e.to); } } } return ret; } /// @brief 直前に流したフローの辺の情報を返す vector get_edges() { vector ret; for(int i=0; in=n; start=n; goal=n+1; mf=MaxFlow(n+2); } /// @brief x[i] = 0 のときコスト zero, x[i] = 1 のときコスト one がかかるという条件を追加する void add_single(int i, ll zero, ll one) { if(zero<=one) { //基本コストがzeroで、iを0から1に変えると+one-zeroされる offset+=zero; mf.add_edge(start,i,one-zero); } else { //基本コストがoneで、iを1から0に変えると-one+zeroされる offset+=one; mf.add_edge(i,goal,zero-one); } } /** * @brief * x[ i ], x[ j ] の組み合わせについて、以下のコストかかるという条件を追加する * | | x[j] = 0 | x[j] = 1 | * | --- | --- | --- | * | x[i] = 0 | a | b | * | x[i] = 1 | c | d | * * @attention b + c >= a + d を要求する */ void add_double(int i, int j, ll a, ll b, ll c, ll d) { assert(b+c>=a+d); offset+=a; add_single(i,0,c-a); add_single(j,0,d-c); mf.add_edge(i,j,b+c-a-d); } vector fukugen() { auto ret=mf.mincut(n+1); ret.pop_back(); ret.pop_back(); return ret; } /// @brief コスト最小値を求める ll solve() { return mf.flow(start,goal)+offset; } private: int n,start,goal; ll offset=0; MaxFlow mf; }; //---------------------------------------------------------- void solve() { ll N; cin>>N; ll INFL=1e12; MoyasuUmeru mu(N); VL P(N); REP(i,N) { cin>>P[i]; mu.add_single(i,0,-P[i]); } ll M; cin>>M; VP E(M); REP(i,M) { ll u,v; cin>>u>>v; u--; v--; mu.add_double(u,v,0,INFL,0,0); E[i]={u,v}; } ll K; cin>>K; vector> sing(K); REP(i,K) { ll a,b,s; cin>>a>>b>>s; a--; b--; mu.add_double(a,b,0,0,0,-s); sing[i]={a,b,s}; } cout<<-mu.solve()<>T; while(T--) solve(); }