思路:
因为颜色的种类范围为1e9,那么肯定不能三重循环dp去得到答案,因为总共也只有1e5数量级的点,所以我们考虑对颜色相同的点进行计算,那么首先我们就要开一个桶,统计每种相同颜色点的各种信息,接下来考虑如何进行计算,我们容易想到,我们可以运用扫描线进行维护,即我们对点的一维进行排序,然后对其中另一维使用数据结构进行维护即可,因为当前点的状态只能由横坐标小于等于自身且纵坐标小于等于自身的点转移过来,所以数据结构我们需要维护另一维的对应信息,因为保证了第一维有序,所以当前点的转化范围即为(1,y),y为自身的纵坐标,从而转化为了查询区间最大值,然后进行单点修改,因此使用线段树进行维护即可。
最后因为要对每种颜色进行维护,所以需要开1e5数量级别的线段树,这里有两种方法,第一种是动态开点,第二种则是只用建一次树,然后每次对使用了节点进行清空即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> ar;
const int mod = 1e9+7;
const int maxv = 4e6 + 5;
// #define endl "\n"
template<const int T>
struct ModInt {
const static int mod = T;
int x;
ModInt(int x = 0) : x(x % mod) {}
ModInt(long long x) : x(int(x % mod)) {}
int val() { return x; }
ModInt operator + (const ModInt &a) const { int x0 = x + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }
ModInt operator - (const ModInt &a) const { int x0 = x - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }
ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
ModInt operator / (const ModInt &a) const { return *this * a.inv(); }
bool operator == (const ModInt &a) const { return x == a.x; };
bool operator != (const ModInt &a) const { return x != a.x; };
void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; }
void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; }
void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; }
void operator /= (const ModInt &a) { *this = *this / a; }
friend ModInt operator + (int y, const ModInt &a){ int x0 = y + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }
friend ModInt operator - (int y, const ModInt &a){ int x0 = y - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }
friend ModInt operator * (int y, const ModInt &a){ return ModInt(1LL * y * a.x % mod);}
friend ModInt operator / (int y, const ModInt &a){ return ModInt(y) / a;}
friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;}
friend istream &operator>>(istream &is, ModInt &t){return is >> t.x;}
ModInt pow(int64_t n) const {
ModInt res(1), mul(x);
while(n){
if (n & 1) res *= mul;
mul *= mul;
n >>= 1;
}
return res;
}
ModInt inv() const {
int a = x, b = mod, u = 1, v = 0;
while (b) {
int t = a / b;
a -= t * b; swap(a, b);
u -= t * v; swap(u, v);
}
if (u < 0) u += mod;
return u;
}
};
int tot,rt[200005];
struct node{
ll sum,lc,rc;
}tree[12000005];
int build(){
tot++;
return tot;
}
void pushup(int x){
tree[x].sum=max(tree[tree[x].lc].sum,tree[tree[x].rc].sum);
return;
}
void change(int x,int l,int r,int w,ll v){
if(l==r){
tree[x].sum=v;
return;
}
int mid=(l+r)/2;
if(w<=mid){
if(!tree[x].lc)tree[x].lc=build();
change(tree[x].lc,l,mid,w,v);
}
else{
if(!tree[x].rc)tree[x].rc=build();
change(tree[x].rc,mid+1,r,w,v);
}
pushup(x);
return;
}
ll query(int x,int l,int r,int l0,int rr){
if(r<l0||l>rr)return 0;
if(l>=l0&&r<=rr){
return tree[x].sum;
}
int mid=(l+r)/2;
ll res=0;
res=max(res,query(tree[x].lc,l,mid,l0,rr));
res=max(res,query(tree[x].rc,mid+1,r,l0,rr));
return res;
}
void solve()
{
tot=0;
int n,m;
cin>>n>>m;
map<int,vector<ar> > mp;
vector<vector<ll> > a(n+5,vector<ll> (m+5)),c(n+5,vector<ll> (m+5));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) cin>>c[i][j];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) cin>>a[i][j];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
mp[c[i][j]].push_back({i,j,a[i][j]});
}
}
ll ans=0;
int cnt=1;
for(auto &[x,v]: mp){
sort(v.begin(),v.end(),[](ar a, ar b)
{
if(a[0]==b[0]) return a[1]<b[1];
return a[0]<b[0];
});
rt[cnt]=build();
for(auto [i,j,val]: v){
ll res=query(rt[cnt],1,n*m,1,j);
change(rt[cnt],1,n*m,j,res+val);
}
ans=max(ans,query(rt[cnt],1,n*m,1,n*m));
cnt++;
}
cout<<ans<<endl;
for(int i=0;i<=tot;i++){
tree[i].lc=tree[i].rc=tree[i].sum=0;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t = 1;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}