CF1416题解

CF1416

Codeforces Round 673 (Div. 1)

CF1416A

link

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 t1000,序列长度 n ≤ 3 × 1 0 5 n\le3\times10^5 n3×105,元素大小 1 ≤ a i ≤ n 1\le a_i\le n 1ain

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,j1],ak=ai 满足 j − i > = k j-i>=k ji>=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

link

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:=aixi,aj:=aj+xi
  • 必须保证操作过程中的任意时刻序列 a a a 中每个元素都非负。
  • 输出时先输出操作次数 k k k,然后输出 k k k 行操作序列。
  • 数据组数 t ≤ 1 0 4 t\le10^4 t104,序列长度 n ≤ 1 0 4 n\le10^4 n104,元素大小 1 ≤ a i ≤ 1 0 5 1\le a_i\le10^5 1ai105

CF1416B题解

翻译里面没有正整数这个重要限制,差评
不难想到,如果 ∑ i = 1 n a i ≢ 0 ( m o d n ) \sum_{i=1}^na_i \not \equiv 0\pmod n i=1nai0(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} ∀1in,ai=nj=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 n1 次将这个和分配到 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 ∀2in,iai 就好办了。
发现如果先让 a 1 a_1 a1 补给 a i a_i ai 一部分数使得 i ∣ a i i|a_i iai,然后再还回来不就好了吗?
但是这样的话 a 1 a_1 a1 会不会不够补的导致无解呢?
然后就回到开头那句话, a i a_i ai正整数 ,也就是说,到 a i a_i ai 的时候, a 1 a_1 a1 至少 i − 1 i-1 i1,也就是说,一定能补上。
没了。

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

link

CF1416C题意

给你一个长度为 n ( n ≤ 3 × 1 0 5 ) n(n\le3\times10^5) n(n3×105) 的数组 a ( a i ≤ 1 0 9 ) a(a_i\le10^9) a(ai109)
现在让你选一个数 x ≤ 1 0 9 x\le10^9 x109,使得 a i ⊕ x {a_i\oplus x} aix 的逆序对最少,如果有多组解要求 x x x 最小。

CF1416C题解

考虑统计这样的数据:

  • r e v i rev_i revi:二进制表示下 i + 1 → 30 i+1\to 30 i+130 位都相等,第 i i i 位是逆序的对数。
  • r e s i res_i resi:二进制表示下 i + 1 → 30 i+1\to 30 i+130 位都相等,第 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

link

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 n2×105,m3×105,q5×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

link

CF1416F题意

对于大小为 n ⋅ m n\cdot m nm 的矩阵 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,j1) 处,依次类推。

定义 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] 1nm105,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+j1(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+j0(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+j1(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,yai,j
  • 4 4 4 类点: i + j ≡ 0 ( m o d 2 ) i+j \equiv 0 \pmod 2 i+j0(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,yai,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,j3 的点 ( i , j ) (i,j) (i,j) 称为必选点,否则为非必选点。
然后就尝试将所有的必选点匹配。
具体如下建图:

  • 对于 o p t i , j ≡ 1 ( m o d 2 ) opt_{i,j} \equiv 1\pmod 2 opti,j1(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,j0(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,j1 就一定满足条件要求。
然后是没匹配的点,只需要任意的找一个比他小的数字出去即可。
不严谨证明:
首先,现在已经尽可能匹配了,也就是说,现在剩下的点,周围任意一个点,要不然不与他相同,要不然已经和另一个点组合成一个新环。当然,根据定义,这个点一定有出去的数字。
现在假设一个点有两种出去的方法,而且现在已经找到了一种解,即一条(或多条)路径经过这个点。
现在尝试让这个点换一个出去的方向,发现对于这个点之后路径上的点没有影响,而对于这个点以及之前路径上的点,因为这个点有方法出去,这个点之后的也都有方法出去,故一定可以。

然后一定注意最大流的写法,只要剩余流量为 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值