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分别填充到对应的行和列上,这样就可以使矩阵元素和最大
#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 1cout<<1<<' '<<i+1<<str<<endl;// type 2cout<<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)
#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() {IOSint 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];} elsepos --;}
// 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 0ops.push_back(make_pair(pos, pos));// now 0ops.push_back(make_pair(pos, pos));// after to 1store[pos] = 1;return;}drep(i, len-1, 1) {// i i i .... i Xdfs(pos, i);}// 1 2 3 ... n-1 Xif (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 0ops.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;
}