codeforce#939 (div2) 题解

C. Nene's Magical Matrix

给一个nxn的矩阵,现在你可以执行一个操作:将数字1-n任意排列,并将这个序列覆盖到矩阵的任意一行或列。操作次数小于 2 n 2n 2n,求矩阵中元素和的最大值。

这个题显然存在一种巧妙的构造方法使得矩阵中元素 a i , j = m a x ( i , j ) a_{i,j} = max(i,j) ai,j=max(i,j)

从n开始到1,将序列 1 , 2 , 3 , 4 ... n 1,2,3,4 \dots n 1,2,3,4...n分别填充到对应的行和列上,这样就可以使矩阵元素和最大

cpp 复制代码
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <time.h>
#include <set>
#include <map>
#include <queue>

#define IOS     ios::sync_with_stdio(0);cin.tie(0);
#define mem(A,B) memset(A,B,sizeof(A));
#define rep(index,start,end) for(int index = start;index < end; index ++)
#define drep(index,start,end) for(int index = start;index >= end; index --)

using namespace std;

const int maxn = 512;

int store[maxn][maxn];
int perm[maxn];
void test(int);
bool check(int);
int main() {
    IOS
    
//    rep(i,1,501) test(i);
//    cout<<"finish"<<endl;
    
    int t;
    cin>>t;
    while(t--) {
        int n;
        cin>>n;
        
        rep(i,0,n) rep(j,0,n) store[i][j] = 0;
        rep(i,1,n+1) perm[i-1] = i;
        string str = "";
        rep(i,0,n) {
            str += ' ';
            str += to_string(perm[i]);
        }
        
        int ans = 0,sum = 0;
        drep(i,n,1) {
            sum += i;
            ans += sum;
        }
        ans = ans*2 - sum;
        int _time = 2*n;
        cout<<ans<<' '<<_time<<endl;
        drep(i,n-1,0) {
            // type 1
            cout<<1<<' '<<i+1<<str<<endl;
            // type 2
            cout<<2<<' '<<i+1<<str<<endl;
        }
    }
    
    return 0;
}
void test(int n) {
    rep(i,1,n+1) perm[i-1] = i;
    
    int _time = 0;
    drep(i,n-1,0) {
        rep(col,0,n)
            store[i][col] = perm[col];
        _time ++;
        
        rep(row,0,n)
            store[row][i] = perm[row];
        _time ++;
    }
    if (_time != 2*n) {
        cout<<n<<"!"<<endl;
    }
    if (!check(n)) {
        cout<<"false"<<endl;
        rep(i,0,n) {
            rep(j,0,n) cout<<store[i][j]<<' ';
            cout<<endl;
        }
    }
}
bool check(int n) {
    bool ans = true;
    rep(i,0,n) {
        rep(j,0,n) {
            ans = ans && (store[i][j] == max(i+1,j+1));
        }
        if (!ans) break;
    }
    return ans;
}

D. Nene and the Mex Operator

M E X ( a l , a l + 1 , ... , a r ) MEX({a_l,a_{l+1},...,a_r}) MEX(al,al+1,...,ar)表示没有出现在这个些数中最小的数。比如 M E X ( 5 , 3 , 4 ) = 2 MEX(5,3,4) = 2 MEX(5,3,4)=2。现在你可以执行不超过 5 ⋅ 1 0 5 5\cdot 10^5 5⋅105次MEX操作,假设每一次对于区间 ( l , r ) (l,r) (l,r)MEX得到的结果是x,令 a i = x , l ≤ i ≤ r a_i = x, l\le i\le r ai=x,l≤i≤r,问能得到的数组的最大值。

由于MEX得到的是未出现的最小实数,所以是不是看起来MEX只能让结果变小呢。实则不然,比如 M E X ( 0 ) = 1 MEX(0) = 1 MEX(0)=1以及 M E X ( 1 , 0 ) = 2 MEX(1,0) = 2 MEX(1,0)=2。所以不难得出对于长度为n的区间,MEX所能使区间最大值为 n 2 n^2 n2。

那么现在我们再来看怎么才能将任意一个长度为n的区间转换为最大值的样式 ( n , n , n ... n ) (n,n,n \dots n) (n,n,n...n)。为了达到这个效果,那么需要前置状态为 ( 1 , 2 , 3 ... n − 1 , 0 ) (1,2,3 \dots n-1, 0) (1,2,3...n−1,0),这样MEX才能使得结果最大。那么怎么才能到达这个状态呢,不妨借助一个中间状态 ( n − 1 , n − 1 , n − 1 ... n − 1 , 0 ) (n-1, n-1, n-1 \dots n-1 ,0) (n−1,n−1,n−1...n−1,0),在这个基础上再去转移到 ( n − 2 , n − 2 , n − 2 ... n − 2 , n − 1 , 0 ) (n-2, n-2, n-2 \dots n-2,n-1,0) (n−2,n−2,n−2...n−2,n−1,0),直到 ( 1 , 2 , 3 ... n − 1 , 0 ) (1,2,3 \dots n-1, 0) (1,2,3...n−1,0)。通过这个流程我们就能构造出最大值。

然后就是关于哪些区间需要我们去构造。因为MEX最大只能得到 n 2 n^2 n2,所以这里可以用dp去计算我们能得到的最大值。官方题解用的是状压dp,但是我没想到,就额外开了一个数组记录前置状态。转移方程
d p [ i ] = m a x ( d p [ i − 1 ] + s t o r e [ i ] , ∑ j = 0 i − 1 d p [ j ] + ( i − j ) 2 ) dp[i] = max(dp[i-1] + store[i], \sum_{j=0}^{i-1} dp[j] + (i-j)^2) dp[i]=max(dp[i−1]+store[i],j=0∑i−1dp[j]+(i−j)2)

cpp 复制代码
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <time.h>
#include <set>
#include <map>
#include <queue>

#define IOS     ios::sync_with_stdio(0);cin.tie(0);
#define re      return 0;
#define mem(A,B) memset(A,B,sizeof(A));
#define rep(index,start,end) for(int index = start;index < end; index ++)
#define drep(index,start,end) for(int index = start;index >= end; index --)

using namespace std;

typedef pair<int, int> pii;

const int maxn = 32;
int pow_table[20] = {0,2};

int store[maxn];
int dp[maxn];
int pre_ind[maxn];
int cnt[maxn];
vector<pii> ops;
void dfs(int,int);
inline int mex(int ,int);
void show(int);
int main() {
    IOS
    
    int n;
    cin>>n;
    rep(i,1,n+1) cin>>store[i];
    
    dp[0] = 0;
    rep(i,1,n+1) {
        if (dp[i] < dp[i-1]+store[i]) {
            dp[i] = dp[i-1]+store[i];
            pre_ind[i] = 0;
        }
        rep(j,0,i) {
            if (dp[i] < dp[j] + (i-j)*(i-j)) {
                dp[i] = dp[j] + (i-j)*(i-j);
                pre_ind[i] = i - j;
            }
        }
    }
    
    int pos = n;
    
    
    pos = n;
    while(pos) {
        int interv = pre_ind[pos];
        if (interv) {
            int s = pos - pre_ind[pos] + 1;
            dfs(s, interv);
            pos -= pre_ind[pos];
        } else
            pos --;
        
    }
//    show(n);
    
    cout<<dp[n]<<' '<<ops.size()<<endl;
    for(auto it = ops.begin();it!=ops.end();it++)
        cout<<it->first<<' '<<it->second<<endl;
    return 0;
}
// return len len len ... len
void dfs(int pos,int len) {
    if (len == 1) {
        if (store[pos]) // to 0
            ops.push_back(make_pair(pos, pos));
        // now 0
        ops.push_back(make_pair(pos, pos));
        // after to 1
        store[pos] = 1;
        return;
    }
    
    drep(i, len-1, 1) {
        // i i i .... i X
        dfs(pos, i);
    }
    // 1 2 3 ... n-1 X
    if (store[pos+len-1])
        ops.push_back(make_pair(pos+len-1, pos+len-1));
    store[pos+len-1] = 0;
    
    // 1 2 3 ... n-1 0
    ops.push_back(make_pair(pos, pos+len-1));
    int num = mex(pos, len);
    rep(i,pos, pos+len) store[i] = num;
}
inline int mex(int pos,int len) {
    rep(i,0,maxn) cnt[i] = 0;
    rep(i,pos, pos+len) {
        int num = store[i];
        if (num<maxn) cnt[num] ++;
    }
    int ind = 0;
    while(cnt[ind]) ind ++;
    return ind;
}
void show(int len) {
    rep(i,1,len+1)
        cout<<store[i]<<' ';
    cout<<endl;
}
相关推荐
xiaoshiguang33 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡3 小时前
【C语言】判断回文
c语言·学习·算法
别NULL3 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇3 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
yuanbenshidiaos5 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习5 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA5 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo5 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc5 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
游是水里的游6 小时前
【算法day20】回溯:子集与全排列问题
算法