CF1416
CF1416A
CF1416A题意
- 定义 一个序列的 k k k 数 为 出现在序列所有长度为 k k k 的子区间内的最小数,如没有数出现在所有长度为 k k k 的子区间内,则 k k k 数为 − 1 -1 −1。
- 给出一个序列,求出对于 k ∈ [ 1 , n ] k\in[1,n] k∈[1,n] 的每一个 k k k 数。
- 数据组数 t ≤ 1000 t\le1000 t≤1000,序列长度 n ≤ 3 × 1 0 5 n\le3\times10^5 n≤3×105,元素大小 1 ≤ a i ≤ n 1\le a_i\le n 1≤ai≤n。
CF1416A题解
对于每一个数
x
x
x,它能够对
k
k
k 做出贡献当且仅当最远的
(
i
,
j
)
(i,j)
(i,j) 点对,
a
i
=
a
j
=
x
,
∀
k
∈
[
i
+
1
,
j
−
1
]
,
a
k
≠
a
i
a_i=a_j=x,\forall k\in[i+1,j-1],a_k\neq a_i
ai=aj=x,∀k∈[i+1,j−1],ak=ai 满足
j
−
i
>
=
k
j-i>=k
j−i>=k。
这玩应想怎么做怎么做,反正我用的差分,时间复杂度
O
(
n
)
O(n)
O(n)。
CF1416A代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 3e5 + 10;
int n, a[maxn];
int ans[maxn];
vector<int> vec[maxn];
signed main(){
int T = read();
while(T--){
n = read();memset(ans,0x3f,sizeof(ans));
for(int i = 1;i <= n;i++){a[i] = read();vec[i].clear();}
for(int i = 1;i <= n;i++)vec[i].push_back(0);
for(int i = 1;i <= n;i++)vec[a[i]].push_back(i);
for(int i = 1;i <= n;i++)vec[i].push_back(n + 1);
for(int i = n;i;i--){
int mx = 0;
for(int j = 0;j + 1 < vec[i].size();j++)
mx = max(mx,vec[i][j + 1] - vec[i][j]);
ans[mx] = i;
}
for(int i = 1;i <= n;i++){
ans[i] = min(ans[i],ans[i - 1]);
if(ans[i] == 0x3f3f3f3f)printf("-1 ");
else printf("%d ",ans[i]);
}
puts("");
}
return 0;
}
CF1416B
CF1416B题意
给出一个序列 a a a,其中每个元素为 正整数 ,求出一个长度不超过 3 n 3n 3n 的操作序列,使序列 a a a 中每个元素相等。
- 定义一次操作为:选出 ( i , j , x ) (i,j,x) (i,j,x) 三元组,满足 i , j i,j i,j 为序列合法下标, x x x 为 1 0 9 10^9 109 以内非负整数,令 a i : = a i − x ⋅ i , a j : = a j + x ⋅ i a_i:= a_i-x\cdot i,a_j:=a_j+x\cdot i ai:=ai−x⋅i,aj:=aj+x⋅i。
- 必须保证操作过程中的任意时刻序列 a a a 中每个元素都非负。
- 输出时先输出操作次数 k k k,然后输出 k k k 行操作序列。
- 数据组数 t ≤ 1 0 4 t\le10^4 t≤104,序列长度 n ≤ 1 0 4 n\le10^4 n≤104,元素大小 1 ≤ a i ≤ 1 0 5 1\le a_i\le10^5 1≤ai≤105。
CF1416B题解
翻译里面没有正整数这个重要限制,差评
不难想到,如果
∑
i
=
1
n
a
i
≢
0
(
m
o
d
n
)
\sum_{i=1}^na_i \not \equiv 0\pmod n
∑i=1nai≡0(modn),那么无解。
否则最终情况一定是
∀
1
≤
i
≤
n
,
a
i
=
∑
j
=
1
n
a
j
n
\forall 1\le i\le n,a_i=\frac{\sum_{j=1}^na_j}{n}
∀1≤i≤n,ai=n∑j=1naj
尝试构造这样一个解。
不难发现,在这个操作中,让
i
=
1
i=1
i=1 可以进行最精确的调整,于是希望
a
1
=
∑
j
=
1
n
a
j
a_1=\sum_{j=1}^na_j
a1=∑j=1naj,然后用
n
−
1
n-1
n−1 次将这个和分配到
a
i
a_i
ai 上
于是就需要构造一个方案让
a
1
=
∑
j
=
1
n
a
j
a_1=\sum_{j=1}^na_j
a1=∑j=1naj。
想到,如果尝试让
∀
2
≤
i
≤
n
,
i
∣
a
i
\forall 2\le i \le n,i|a_i
∀2≤i≤n,i∣ai 就好办了。
发现如果先让
a
1
a_1
a1 补给
a
i
a_i
ai 一部分数使得
i
∣
a
i
i|a_i
i∣ai,然后再还回来不就好了吗?
但是这样的话
a
1
a_1
a1 会不会不够补的导致无解呢?
然后就回到开头那句话,
a
i
a_i
ai 是 正整数 ,也就是说,到
a
i
a_i
ai 的时候,
a
1
a_1
a1 至少 是
i
−
1
i-1
i−1,也就是说,一定能补上。
没了。
CF1416B代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0'&& ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e4 + 10;
int n, a[maxn];
vector<pair<pair<int,int>,int> >opt;
signed main(){
int T = read();
while(T--){
n = read(); opt.clear(); int sum = 0;
for(int i = 1;i <= n;i++)sum += (a[i] = read());
if(sum % n){puts("-1");continue;}
for(int i = 2;i <= n;i++){
if(a[i] % i){
int x = i - a[i] % i;
opt.push_back(make_pair(make_pair(1,i),x));
a[i] += x;a[1] -= x;
}
int x = a[i] / i;a[1] += a[i];
opt.push_back(make_pair(make_pair(i,1),x));
}
for(int i = 2;i <= n;i++)
opt.push_back(make_pair(make_pair(1,i),sum / n));
printf("%d\n",opt.size());
for(auto i : opt)printf("%d %d %d\n",i.first.first,i.first.second,i.second);
}
return 0;
}
CF1416C
CF1416C题意
给你一个长度为
n
(
n
≤
3
×
1
0
5
)
n(n\le3\times10^5)
n(n≤3×105) 的数组
a
(
a
i
≤
1
0
9
)
a(a_i\le10^9)
a(ai≤109)。
现在让你选一个数
x
≤
1
0
9
x\le10^9
x≤109,使得
a
i
⊕
x
{a_i\oplus x}
ai⊕x 的逆序对最少,如果有多组解要求
x
x
x 最小。
CF1416C题解
考虑统计这样的数据:
- r e v i rev_i revi:二进制表示下 i + 1 → 30 i+1\to 30 i+1→30 位都相等,第 i i i 位是逆序的对数。
- r e s i res_i resi:二进制表示下 i + 1 → 30 i+1\to 30 i+1→30 位都相等,第 i i i 位是正序的对数。
于是对每一位进行贪心:
- 如果 r e v i > r e s i rev_i > res_i revi>resi,那么让这一位是 1 1 1 显然更优。
- 否则让这一位是 0 0 0。
这东西显然可以 01 t r i e 01trie 01trie 维护。
CF1416C代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 3e5 + 10;
int n;
ll res[33], rev[33];
int trie[maxn * 31][2], tot = 1;
ll cnt[maxn * 31];
void insert(int x){
int u = 1;
for(int i = 31;i + 1;i--){
bool v = (x >> i) & 1;
if(!trie[u][v])trie[u][v] = ++tot;
if(!v)rev[i] += cnt[trie[u][1]];
else res[i] += cnt[trie[u][0]];
u = trie[u][v];cnt[u]++;
}
}
signed main(){
n = read();int x = 0;ll ans = 0;
for(int i = 1;i <= n;i++)insert(read());
for(int i = 31;i + 1;i--){
if(rev[i] > res[i]){
x |= (1 << i);
ans += res[i];
}
else ans += rev[i];
}
printf("%lld %d\n",ans,x);
return 0;
}
CF1416D
CF1416D题意
给出一个 n n n 点 m m m 边的无向图,每个点有一个权值 p i p_i pi,保证 p i p_i pi 互不相同,现在有两种操作:
- 1 u 1\space u 1 u:找当前 u u u 所在联通块中,最大的 p x p_x px,输出 p x p_x px 并将其修改为 0 0 0。
- 2 u 2\space u 2 u:删除 u u u 这条边。
n ≤ 2 × 1 0 5 , m ≤ 3 × 1 0 5 , q ≤ 5 × 1 0 5 n\le2\times10^5,m\le3\times10^5,q\le5\times10^5 n≤2×105,m≤3×105,q≤5×105
CF1416D题解
受不了了为什么一天做三道数据结构啊(
受不了了为什么一天做两道码量超过5K的神秘图论啊(
考虑到删除不太好做,于是将每条边记录删除时间(如果一直没删除就是
m
+
1
m+1
m+1)。
然后跑kruskal
重构树(从大到小加入边)。
发现这个重构树有着很好的性质:
如果将每个节点的时间设为
t
i
m
i
tim_i
timi,那么对于查询操作,向上找到最大的
t
i
m
t
>
u
tim_{t}>u
timt>u,那么这个
t
t
t 的子树中所有点就是这个查询中能到达的所有点。
子树中最大值,不难想到dfs
序,然后线段树维护操作即可。
不过码量达到了惊人的4.92KB
(或许是我写的太冗杂了\kel),还有对于边界的判定,慢慢写吧。
CF1416D代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 3e5 + 10,maxq = 5e5 + 10;
struct edge{
int fr, to, tim;
edge(int fr = 0,int to = 0,int tim = 0):fr(fr),to(to),tim(tim){}
friend bool operator < (edge a,edge b){return a.tim > b.tim;}
}e[maxn];
int n, m, q;
int val[maxq], tim[maxq];
pair<int,int> opt[maxq];int tot;
vector<int> edg[maxq];
int cntpoint, id[maxq];
struct DSU{
int fa[maxq];
void init(int x){for(int i = 1;i <= x;i++)fa[i] = i;}
int getf(int x){return fa[x] == x ? x : fa[x] = getf(fa[x]);}
}dsu;
int fa[21][maxq], idx, dfn[maxq], siz[maxq], rev[maxq];
void dfs(int u,int f){
dfn[u] = ++idx;siz[u] = 1;rev[idx] = u;
for(int v : edg[u])
if(v != f){
if(tim[v] == 0)tim[v] = tim[u];
dfs(v, u); siz[u] += siz[v];
}
}
struct Segment_Tree{
struct node{
int maxx, pos;
node(int mx = 0,int pos = 0):maxx(mx),pos(pos){}
}d[maxq << 2];
node mergenode(node l,node r){return node(max(l.maxx,r.maxx),((l.maxx > r.maxx) ? l.pos : r.pos));}
void build(int l = 1,int r = idx,int p = 1){
if(l == r){d[p] = node(val[rev[l]],l);return;}
int mid = (l + r) >> 1;
build(l,mid,p << 1);build(mid + 1,r,p << 1 | 1);
d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
}
node query(int l,int r,int s,int t,int p){
if(s <= l && r <= t)return d[p];
int mid = (l + r) >> 1;
if(t <= mid)return query(l,mid,s,t,p << 1);
if(mid < s)return query(mid + 1,r,s,t,p << 1 | 1);
return mergenode(query(l,mid,s,t,p << 1),query(mid + 1,r,s,t,p << 1 | 1));
}
void update(int l,int r,int pos,int p,int upd){
if(l == r && l == pos){d[p] = node(upd,0);return;}
int mid = (l + r) >> 1;
if(pos <= mid)update(l,mid,pos,p << 1,upd);
else update(mid + 1,r,pos,p << 1 | 1,upd);
d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
}
node query(int s,int t){return query(1,idx,s,t,1);}
void update(int pos,int upd){update(1,idx,pos,1,upd);}
void DEBUG(int l = 1,int r = idx,int p = 1){
printf("l = %d r = %d p = %d,maxx = %d,pos = %d\n",l,r,p,d[p].maxx,d[p].pos);
if(l == r)return;
int mid = (l + r) >> 1;
DEBUG(l,mid,p << 1);DEBUG(mid + 1,r,p << 1 | 1);
}
}tree;
signed main(){
n = read(); m = read(); q = read();int u, v;
for(int i = 1;i <= n;i++){val[i] = read();fa[0][i] = i;}
for(int i = 1;i <= m;i++){
u = read(); v = read();
e[i] = edge(u, v, m + 2);
}
for(int i = 1;i <= q;i++){
u = read(); v = read();
if(u == 1)opt[i - tot] = make_pair(v,tot);
else e[v].tim = ++tot;
}
sort(e + 1,e + 1 + m);dsu.init(n + m);cntpoint = n;
for(int i = 1;i <= m;i++){
int fu = dsu.getf(e[i].fr), fv = dsu.getf(e[i].to);
// printf("%d %d %d\n",e[i].fr,e[i].to,e[i].tim);
if(fu == fv)continue; ++cntpoint;
// puts("added");
dsu.fa[fu] = dsu.fa[fv] = cntpoint;
tim[cntpoint] = e[i].tim;val[cntpoint] = 0;
edg[cntpoint].push_back(fu);edg[cntpoint].push_back(fv);
fa[0][fu] = fa[0][fv] = cntpoint;
fa[0][cntpoint] = cntpoint;
}
for(int i = 1;i <= 20;i++)
for(int j = 1;j <= cntpoint;j++)
fa[i][j] = fa[i - 1][fa[i - 1][j]];
for(int i = 1;i <= cntpoint;i++){
if(!dfn[i]){
u = i;
for(int j = 20;j + 1;j--)
if(fa[j][u] != u)u = fa[j][u];
u = fa[0][u]; dfs(u, u);
}
}
// for(int i = 1;i <= cntpoint;i++){
// printf("i = %d, siz[i] = %d, tim[i] = %d,val[i] = %d,dfn[i] = %d, rev[i] = %d,",i,siz[i],tim[i],val[i],dfn[i],rev[i]);
// for(int j = 0;j <= 3;j++)printf("fa[%d][%d]=%d,",j,i,fa[j][i]);puts("");
// for(int v : edg[i]) printf("-> %d ", v);puts("");
// }
tree.build();
for(int i = 1;i <= q - tot;i++){
u = opt[i].first, v = opt[i].second;
// printf("query :%d %d\n",u, v);
if(tim[u] <= v){
printf("%d\n",tree.query(dfn[u],dfn[u]).maxx);
tree.update(dfn[u],0);
continue;
}
for(int i = 20;i + 1;i--)
if(tim[fa[i][u]] > v)
u = fa[i][u];
// u = fa[0][u];
// puts("Before query"); tree.DEBUG();
// printf("top = %d\n",u);
Segment_Tree::node tmp = tree.query(dfn[u],dfn[u] + siz[u] - 1);
printf("%d\n",tmp.maxx);
// puts("Before update");tree.DEBUG();
// printf("pos = %d,[%d,%d]\n",tmp.pos,dfn[u],dfn[u] + siz[u] - 1);
if(tmp.maxx) tree.update(tmp.pos,0);
// puts("After update");tree.DEBUG();
}
return 0;
}
CF1416F
CF1416F题意
对于大小为
n
⋅
m
n\cdot m
n⋅m 的矩阵
A
A
A 和
B
B
B,其中
A
A
A 的每个元素为一个权值
w
(
i
,
j
)
w(i,j)
w(i,j),
B
B
B 的每个元素为一个方向 L/R/D/U
初始你在 ( i , j ) (i,j) (i,j),若 B i , j = L B_{i,j}=L Bi,j=L,你可以走到 ( i , j − 1 ) (i,j-1) (i,j−1) 处,依次类推。
定义 S i , j S_{i,j} Si,j 表示从 ( i , j ) (i,j) (i,j) 出发能够到达的点的 A i , j A_{i,j} Ai,j 的和。
给定矩阵 S S S,构造 A A A 和 B B B 使得其生成的矩阵为 S S S
A A A 的每个元素均为正整数。
1 ≤ n ⋅ m ≤ 1 0 5 , S i , j ∈ [ 2 , 1 0 9 ] 1\le n\cdot m\le 10^5,S_{i,j}\in [2,10^9] 1≤n⋅m≤105,Si,j∈[2,109]
CF1416F题解
SBEric网络流写错了,学了一年的网络流模板都是错的。
我写了9.79kb代码,逆天
不难发现,这个路径一定是沿着数字单调递减的前进,直到走进一个环中为止,这个环的所有数相同。
然后发现如果按照单调不增的条件连有向边,那么这个东西构成一个内向基环树森林。
将整张图黑白染色之后,一个环中有相同数量的黑点和白点,且将一个大环拆成若干个小环一定不会使答案更劣。
然后将所有点分成四种:
- 1 1 1 类点: i + j ≡ 1 ( m o d 2 ) i+j \equiv 1 \pmod 2 i+j≡1(mod2),且 ∃ ( x , y ) \exists (x,y) ∃(x,y) 与 ( i , j ) (i,j) (i,j) 相邻满足 a x , y < a i , j a_{x,y}<a_{i,j} ax,y<ai,j。
- 2 2 2 类点: i + j ≡ 0 ( m o d 2 ) i+j \equiv 0 \pmod 2 i+j≡0(mod2),且 ∃ ( x , y ) \exists (x,y) ∃(x,y) 与 ( i , j ) (i,j) (i,j) 相邻满足 a x , y < a i , j a_{x,y}<a_{i,j} ax,y<ai,j。
- 3 3 3 类点: i + j ≡ 1 ( m o d 2 ) i+j \equiv 1 \pmod 2 i+j≡1(mod2),且 ∃ ( x , y ) \exists (x,y) ∃(x,y) 与 ( i , j ) (i,j) (i,j) 相邻满足 a x , y = a i , j a_{x,y}=a_{i,j} ax,y=ai,j,但 ∀ ( x , y ) \forall (x,y) ∀(x,y) 与 ( i , j ) (i,j) (i,j) 相邻满足 a x , y ≥ a i , j a_{x,y}\ge a_{i,j} ax,y≥ai,j。
- 4 4 4 类点: i + j ≡ 0 ( m o d 2 ) i+j \equiv 0 \pmod 2 i+j≡0(mod2),且 ∃ ( x , y ) \exists (x,y) ∃(x,y) 与 ( i , j ) (i,j) (i,j) 相邻满足 a x , y = a i , j a_{x,y}=a_{i,j} ax,y=ai,j,但 ∀ ( x , y ) \forall (x,y) ∀(x,y) 与 ( i , j ) (i,j) (i,j) 相邻满足 a x , y ≥ a i , j a_{x,y}\ge a_{i,j} ax,y≥ai,j。
设
o
p
t
i
,
j
opt_{i,j}
opti,j 表示
(
i
,
j
)
(i,j)
(i,j) 的类型。
那么我们将
o
p
t
i
,
j
≥
3
opt_{i,j}\ge 3
opti,j≥3 的点
(
i
,
j
)
(i,j)
(i,j) 称为必选点,否则为非必选点。
然后就尝试将所有的必选点匹配。
具体如下建图:
- 对于 o p t i , j ≡ 1 ( m o d 2 ) opt_{i,j} \equiv 1\pmod 2 opti,j≡1(mod2) 的点,连接 S → ( i , j ) S\to (i,j) S→(i,j),容量为 [ ( o p t i , j > = 3 ) , 1 ] [(opt_{i,j}>=3),1] [(opti,j>=3),1] 的边。
- 对于 o p t i , j ≡ 0 ( m o d 2 ) opt_{i,j} \equiv 0\pmod 2 opti,j≡0(mod2) 的点,连接 ( i , j ) → T (i,j)\to T (i,j)→T,容量为 [ ( o p t i , j > = 3 ) , 1 ] [(opt_{i,j}>=3),1] [(opti,j>=3),1] 的边。
- 对于 a i , j = = a x , y , ( i , j ) a_{i,j}==a_{x,y},(i,j) ai,j==ax,y,(i,j) 与 ( x , y ) (x,y) (x,y) 相邻,连接 ( i , j ) → ( x , y ) (i,j)\to (x,y) (i,j)→(x,y),容量为 [ 0 , 1 ] [0,1] [0,1] 的边。
建图思路:我们希望所有的必选点一定被匹配,其余的非必选点尽可能的匹配,但是如果跑匈牙利的话会T飞的,于是尝试构造上图,然后只要所有限制都满足,那么一定满足我们的需求,否则无解。
然后跑有源汇上下界最大流【模板】即可。
这个时候,我们得到了所有匹配的黑白点,让其中一个的
a
i
,
j
a_{i,j}
ai,j 是
1
1
1,另一个是
S
i
,
j
−
1
S_{i,j}-1
Si,j−1 就一定满足条件要求。
然后是没匹配的点,只需要任意的找一个比他小的数字出去即可。
不严谨证明:
首先,现在已经尽可能匹配了,也就是说,现在剩下的点,周围任意一个点,要不然不与他相同,要不然已经和另一个点组合成一个新环。当然,根据定义,这个点一定有出去的数字。
现在假设一个点有两种出去的方法,而且现在已经找到了一种解,即一条(或多条)路径经过这个点。
现在尝试让这个点换一个出去的方向,发现对于这个点之后路径上的点没有影响,而对于这个点以及之前路径上的点,因为这个点有方法出去,这个点之后的也都有方法出去,故一定可以。
然后一定注意最大流的写法,只要剩余流量为 0 0 0,那就直接return,否则会TLE的。
CF1416F代码
献上丑陋的9.79KB
代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 5e5 + 10,INF = 0x7f3f3f3f;
const int dx[4] = { 0,-1, 0, 1};
const int dy[4] = {-1, 0, 1, 0};
int a[maxn], typ[maxn];
int ans[maxn], opt[maxn];//0 => L;1 =>U;2 =>R 3=> D
pair<int,int> pos[maxn];
int n, m;
// inline int getid(int x,int y){return (y) * (n + 2) + x;}
#define getid(x, y) ((y) * (n + 2) + (x))
struct edge1{
int from, to, L, R;
edge1(int fr = 0,int to = 0,int L = 0,int R = 0):from(fr),to(to),L(L),R(R){}
}e1[maxn << 3];int totedg;
int in[maxn], out[maxn];
struct edge{
int to, nexte, cap, flow;
edge(int to = 0,int ne = 0,int cap = 0,int flow = 0):to(to),nexte(ne),cap(cap),flow(flow){}
}e[maxn << 4];
int tot = 1, head[maxn << 1];
void add(int u,int v,int cap){e[++tot] = edge(v,head[u],cap);head[u] = tot;}
void addd(int u,int v,int cap){add(u, v, cap);add(v, u, 0);}
int cur[maxn], dis[maxn];
bool book[maxn]; queue<int> que;
int dfs(int u,int flow,int T){
if(u == T || !flow)return flow; int res = 0;
for(int i = cur[u];i && flow;i = e[i].nexte){
int v = e[i].to;cur[u] = i;
if(e[i].cap > e[i].flow && dis[v] == dis[u] + 1){
int tmp = dfs(v,min(flow,e[i].cap - e[i].flow),T);
if(!tmp){dis[v] = INF;continue;}
e[i].flow += tmp;e[i ^ 1].flow -= tmp;
flow -= tmp;res += tmp;
if(!flow)return res;
}
}
return res;
}
bool bfs(int S,int T){
while(!que.empty())que.pop();
for(int i = 1;i <= getid(n,m) + 4;i++){cur[i] = book[i] = 0;dis[i] = INF;}
que.push(S);dis[S] = 0;cur[S] = head[S];book[S] = 1;
while(!que.empty()){
int u = que.front();que.pop();book[u] = 0;
for(int i = head[u];i;i = e[i].nexte){
int v = e[i].to;
if(dis[v] == INF && e[i].cap > e[i].flow){
dis[v] = dis[u] + 1;cur[v] = head[v];
if(!book[v]){que.push(v);book[v] = 1;}
}
}
}
return dis[T] != INF;
}
int Dinic(int S,int T){
int mxflow = 0;
while(bfs(S,T))mxflow += dfs(S,INF,T);
return mxflow;
}
void solve(){
totedg = 0;tot = 1;
n = read(); m = read();
for(int i = 1;i <= getid(n + 1,m + 1) + 4;i++)head[i] = in[i] = out[i] = opt[i] = ans[i] = 0;
for(int i = 0;i <= n + 1;i++)
for(int j = 0;j <= m + 1;j++)
pos[getid(i, j)] = make_pair(i, j);
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++)
a[getid(i,j)] = read();
// puts("Step -5");
for(int i = 0;i <= n + 1;i++)a[getid(i,0)] = a[getid(i,m + 1)] = INF;
for(int i = 0;i <= m + 1;i++)a[getid(0,i)] = a[getid(n + 1,i)] = INF;
// puts("Step -4");
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if((i + j) & 1)typ[getid(i, j)] = 2;
else typ[getid(i,j)] = 1;
int flag = 1;
for(int k = 0;k < 4 && flag;k++)
flag &= (a[getid(dx[k] + i,dy[k] + j)] > a[getid(i,j)]);
if(flag){puts("NO");return;}
flag = 1;
for(int k = 0;k < 4 && flag;k++)
flag &= (a[getid(dx[k] + i,dy[k] + j)] >= a[getid(i,j)]);
typ[getid(i,j)] += flag * 2;
}
}
// puts("Step -3");
int S = getid(n, m) + 1, T = getid(n, m) + 2, ss = getid(n, m) + 3, tt = getid(n, m) + 4;
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(typ[getid(i,j)] & 1){//奇数点
if(typ[getid(i,j)] >= 3)//必须点
e1[++totedg] = edge1(S,getid(i,j),1,1);
else//非必须点
e1[++totedg] = edge1(S,getid(i,j),0,1);
for(int k = 0;k < 4;k++)//只从奇数点就可以把所有需要连接的边全部连接
if(a[getid(i,j)] == a[getid(i + dx[k],j + dy[k])])
e1[++totedg] = edge1(getid(i, j),getid(i + dx[k],j + dy[k]),0,1);
}
else{//偶数点
if(typ[getid(i,j)] >= 3)//必须点
e1[++totedg] = edge1(getid(i,j),T,1,1);
else//非必须点
e1[++totedg] = edge1(getid(i,j),T,0,1);
}
}
}
// puts("Step -2");
e1[++totedg] = edge1(T,S,0,INF);
int sum = 0, delid = 0;
for(int i = 1;i <= totedg;i++){
in[e1[i].to] += e1[i].L; out[e1[i].from] += e1[i].L;
if(e1[i].R - e1[i].L) addd(e1[i].from,e1[i].to,e1[i].R - e1[i].L);
if(i == totedg)delid = tot - 1;
}
for(int x = 1;x <= n;x++)
for(int y = 1;y <= m;y++){
int i = getid(x, y);
if(in[i] > out[i])addd(ss,i,in[i] - out[i]),sum += in[i] - out[i];
if(in[i] < out[i])addd(i,tt,out[i] - in[i]);
}
if(in[S] > out[S])addd(ss,S,in[S] - out[S]),sum += in[S] - out[S];
if(in[S] < out[S])addd(S,tt,out[S] - in[S]);
if(in[T] > out[T])addd(ss,T,in[T] - out[T]),sum += in[T] - out[T];
if(in[T] < out[T])addd(T,tt,out[T] - in[T]);
// puts("Step 0");
int res = Dinic(ss,tt);
// puts("Step 1");
// printf("res = %d sum = %d\n",res,sum);
if(res != sum){puts("NO");return;}
// printf("S = %d T = %d\n",S,T);
// printf("delid = %d, fr = %d, to = %d\n",delid,e[delid ^ 1].to,e[delid].to);
e[delid].cap = e[delid ^ 1].cap = 0;
e[delid].flow = e[delid ^ 1].flow = 0;
Dinic(S,T);
// puts("Step 2");
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(typ[getid(i,j)] & 1){//奇数点
//老样子,还是从奇数点找双元环,偶数点只是判定是否有解即可
int chose = 0, nxt = 0;
for(int ed = head[getid(i,j)];ed;ed = e[ed].nexte)
if(e[ed].flow == 1){nxt = e[ed].to;chose = 1;break;}
if(typ[getid(i,j)] >= 3){//必须点
if(!chose){puts("NO");return;}
else{
int x = pos[nxt].first, y = pos[nxt].second;
if(i - x == 0){
if(j - y == 1){
opt[getid(i,j)] = 0; opt[getid(x,y)] = 2;
ans[getid(i,j)] = 1; ans[getid(x,y)] = a[getid(i,j)] - 1;
}
else{
opt[getid(i,j)] = 2; opt[getid(x,y)] = 0;
ans[getid(i,j)] = 1; ans[getid(x,y)] = a[getid(i,j)] - 1;
}
}
else{
if(i - x == 1){
opt[getid(i,j)] = 1; opt[getid(x,y)] = 3;
ans[getid(i,j)] = 1; ans[getid(x,y)] = a[getid(i,j)] - 1;
}
else{
opt[getid(i,j)] = 3; opt[getid(x,y)] = 1;
ans[getid(i,j)] = 1; ans[getid(x,y)] = a[getid(i,j)] - 1;
}
}
}
}
else{//非必须点
if(chose){
int x = pos[nxt].first, y = pos[nxt].second;
if(i - x == 0){
if(j - y == 1){
opt[getid(i,j)] = 0; opt[getid(x,y)] = 2;
ans[getid(i,j)] = 1; ans[getid(x,y)] = a[getid(i,j)] - 1;
}
else{
opt[getid(i,j)] = 2; opt[getid(x,y)] = 0;
ans[getid(i,j)] = 1; ans[getid(x,y)] = a[getid(i,j)] - 1;
}
}
else{
if(i - x == 1){
opt[getid(i,j)] = 1; opt[getid(x,y)] = 3;
ans[getid(i,j)] = 1; ans[getid(x,y)] = a[getid(i,j)] - 1;
}
else{
opt[getid(i,j)] = 3; opt[getid(x,y)] = 1;
ans[getid(i,j)] = 1; ans[getid(x,y)] = a[getid(i,j)] - 1;
}
}
}
}
}
else{//偶数点
int chose = 0;
for(int ed = head[getid(i,j)];ed;ed = e[ed].nexte)
if(e[ed].flow == 1){chose = 1;break;}
if(typ[getid(i,j)] >= 3)//必须点
if(!chose){puts("NO");return;}
}
}
}
for(int i = 1;i <= n;i++)//找其他没有限制的点
for(int j = 1;j <= m;j++){
if(ans[getid(i,j)])continue;
for(int k = 0;k < 4;k++){
if(a[getid(i + dx[k],j + dy[k])] < a[getid(i,j)]){
opt[getid(i,j)] = k;
ans[getid(i,j)] = a[getid(i,j)] - a[getid(i + dx[k],j + dy[k])];
}
}
}
puts("YES");
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++)
printf("%d ",ans[getid(i,j)]);
puts("");
}
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(opt[getid(i,j)] == 0)putchar('L');
if(opt[getid(i,j)] == 1)putchar('U');
if(opt[getid(i,j)] == 2)putchar('R');
if(opt[getid(i,j)] == 3)putchar('D');
putchar(' ');
}
puts("");
}
}
signed main(){
int T = read();
while(T--)solve();
return 0;
}