CF1416题解
CF1416
CF1416A
CF1416A题意
- 定义 一个序列的 \(k\) 数 为 出现在序列所有长度为 \(k\) 的子区间内的最小数,如没有数出现在所有长度为 \(k\) 的子区间内,则 \(k\) 数为 \(-1\)。
- 给出一个序列,求出对于 \(k\in[1,n]\) 的每一个 \(k\) 数。
- 数据组数 \(t\le1000\),序列长度 \(n\le3\times10^5\),元素大小 \(1\le a_i\le n\)。
CF1416A题解
对于每一个数 \(x\),它能够对 \(k\) 做出贡献当且仅当最远的 \((i,j)\) 点对,\(a_i=a_j=x,\forall k\in[i+1,j-1],a_k\neq a_i\) 满足 \(j-i>=k\)。
这玩应想怎么做怎么做,反正我用的差分,时间复杂度 \(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\),其中每个元素为 正整数 ,求出一个长度不超过 \(3n\) 的操作序列,使序列 \(a\) 中每个元素相等。
- 定义一次操作为:选出 \((i,j,x)\) 三元组,满足 \(i,j\) 为序列合法下标,\(x\) 为 \(10^9\) 以内非负整数,令 \(a_i:= a_i-x\cdot i,a_j:=a_j+x\cdot i\)。
- 必须保证操作过程中的任意时刻序列 \(a\) 中每个元素都非负。
- 输出时先输出操作次数 \(k\),然后输出 \(k\) 行操作序列。
- 数据组数 \(t\le10^4\),序列长度 \(n\le10^4\),元素大小 \(1\le a_i\le10^5\)。
CF1416B题解
翻译里面没有正整数这个重要限制,差评
不难想到,如果 \(\sum_{i=1}^na_i \not \equiv 0\pmod n\),那么无解。
否则最终情况一定是 $$\forall 1\le i\le n,a_i=\frac{\sum_{j=1}^na_j}{n}$$
尝试构造这样一个解。
不难发现,在这个操作中,让 \(i=1\) 可以进行最精确的调整,于是希望 \(a_1=\sum_{j=1}^na_j\),然后用 \(n-1\) 次将这个和分配到 \(a_i\) 上
于是就需要构造一个方案让 \(a_1=\sum_{j=1}^na_j\)。
想到,如果尝试让 \(\forall 2\le i \le n,i|a_i\) 就好办了。
发现如果先让 \(a_1\) 补给 \(a_i\) 一部分数使得 \(i|a_i\),然后再还回来不就好了吗?
但是这样的话 \(a_1\) 会不会不够补的导致无解呢?
然后就回到开头那句话,\(a_i\) 是 正整数 ,也就是说,到 \(a_i\) 的时候,\(a_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\le3\times10^5)\) 的数组 \(a(a_i\le10^9)\)。
现在让你选一个数 \(x\le10^9\),使得 \({a_i\oplus x}\) 的逆序对最少,如果有多组解要求 \(x\) 最小。
CF1416C题解
考虑统计这样的数据:
- \(rev_i\):二进制表示下 \(i+1\to 30\) 位都相等,第 \(i\) 位是逆序的对数。
- \(res_i\):二进制表示下 \(i+1\to 30\) 位都相等,第 \(i\) 位是正序的对数。
于是对每一位进行贪心:
- 如果 \(rev_i > res_i\),那么让这一位是 \(1\) 显然更优。
- 否则让这一位是 \(0\)。
这东西显然可以 \(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\) 点 \(m\) 边的无向图,每个点有一个权值 \(p_i\),保证 \(p_i\) 互不相同,现在有两种操作:
- \(1\space u\):找当前 \(u\) 所在联通块中,最大的 \(p_x\),输出 \(p_x\) 并将其修改为 \(0\)。
- \(2\space u\):删除 \(u\) 这条边。
\(n\le2\times10^5,m\le3\times10^5,q\le5\times10^5\)
CF1416D题解
受不了了为什么一天做三道数据结构啊(
受不了了为什么一天做两道码量超过5K的神秘图论啊(
考虑到删除不太好做,于是将每条边记录删除时间(如果一直没删除就是 \(m+1\))。
然后跑kruskal重构树(从大到小加入边)。
发现这个重构树有着很好的性质:
如果将每个节点的时间设为 \(tim_i\),那么对于查询操作,向上找到最大的 \(tim_{t}>u\),那么这个 \(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\cdot m\) 的矩阵 \(A\) 和 \(B\),其中 \(A\) 的每个元素为一个权值 \(w(i,j)\),\(B\) 的每个元素为一个方向 L/R/D/U
初始你在 \((i,j)\),若 \(B_{i,j}=L\),你可以走到 \((i,j-1)\) 处,依次类推。
定义 \(S_{i,j}\) 表示从 \((i,j)\) 出发能够到达的点的 \(A_{i,j}\) 的和。
给定矩阵 \(S\),构造 \(A\) 和 \(B\) 使得其生成的矩阵为 \(S\)
\(A\) 的每个元素均为正整数。
\(1\le n\cdot m\le 10^5,S_{i,j}\in [2,10^9]\)
CF1416F题解
SBEric网络流写错了,学了一年的网络流模板都是错的。
我写了9.79kb代码,逆天
不难发现,这个路径一定是沿着数字单调递减的前进,直到走进一个环中为止,这个环的所有数相同。
然后发现如果按照单调不增的条件连有向边,那么这个东西构成一个内向基环树森林。
将整张图黑白染色之后,一个环中有相同数量的黑点和白点,且将一个大环拆成若干个小环一定不会使答案更劣。
然后将所有点分成四种:
- \(1\) 类点:\(i+j \equiv 1 \pmod 2\),且 \(\exists (x,y)\) 与 \((i,j)\) 相邻满足 \(a_{x,y}<a_{i,j}\)。
- \(2\) 类点:\(i+j \equiv 0 \pmod 2\),且 \(\exists (x,y)\) 与 \((i,j)\) 相邻满足 \(a_{x,y}<a_{i,j}\)。
- \(3\) 类点:\(i+j \equiv 1 \pmod 2\),且 \(\exists (x,y)\) 与 \((i,j)\) 相邻满足 \(a_{x,y}=a_{i,j}\),但 \(\forall (x,y)\) 与 \((i,j)\) 相邻满足 \(a_{x,y}\ge a_{i,j}\)。
- \(4\) 类点:\(i+j \equiv 0 \pmod 2\),且 \(\exists (x,y)\) 与 \((i,j)\) 相邻满足 \(a_{x,y}=a_{i,j}\),但 \(\forall (x,y)\) 与 \((i,j)\) 相邻满足 \(a_{x,y}\ge a_{i,j}\)。
设 \(opt_{i,j}\) 表示 \((i,j)\) 的类型。
那么我们将 \(opt_{i,j}\ge 3\) 的点 \((i,j)\) 称为必选点,否则为非必选点。
然后就尝试将所有的必选点匹配。
具体如下建图:
- 对于 \(opt_{i,j} \equiv 1\pmod 2\) 的点,连接 \(S\to (i,j)\),容量为 \([(opt_{i,j}>=3),1]\) 的边。
- 对于 \(opt_{i,j} \equiv 0\pmod 2\) 的点,连接 \((i,j)\to T\),容量为 \([(opt_{i,j}>=3),1]\) 的边。
- 对于 \(a_{i,j}==a_{x,y},(i,j)\) 与 \((x,y)\) 相邻,连接 \((i,j)\to (x,y)\),容量为 \([0,1]\) 的边。
建图思路:我们希望所有的必选点一定被匹配,其余的非必选点尽可能的匹配,但是如果跑匈牙利的话会T飞的,于是尝试构造上图,然后只要所有限制都满足,那么一定满足我们的需求,否则无解。
然后跑有源汇上下界最大流【模板】即可。
这个时候,我们得到了所有匹配的黑白点,让其中一个的 \(a_{i,j}\) 是 \(1\),另一个是 \(S_{i,j}-1\) 就一定满足条件要求。
然后是没匹配的点,只需要任意的找一个比他小的数字出去即可。
不严谨证明:
首先,现在已经尽可能匹配了,也就是说,现在剩下的点,周围任意一个点,要不然不与他相同,要不然已经和另一个点组合成一个新环。当然,根据定义,这个点一定有出去的数字。
现在假设一个点有两种出去的方法,而且现在已经找到了一种解,即一条(或多条)路径经过这个点。
现在尝试让这个点换一个出去的方向,发现对于这个点之后路径上的点没有影响,而对于这个点以及之前路径上的点,因为这个点有方法出去,这个点之后的也都有方法出去,故一定可以。
然后一定注意最大流的写法,只要剩余流量为 \(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;
}

浙公网安备 33010602011771号