2019年第一篇文章 (。・∀・)ノ゙
Problem
bzoj无良权限题,拿学长的号交的
题目概要:给定一个(n imes n)的矩阵。令(x=frac {n+1}2)。可以进行任意次以下操作:选择一个(x imes x)的子矩阵,将其中所有数乘上(-1)。求操作后矩阵元素和的最大值。(nleq 33)且为奇数
Solution
这道题挺有意思的,两道题的思想一拼就成另外一道题了,而且毫不让人厌烦
发现数据范围十分有意思(nleq 33),这个复杂度不是搜索就是折半搜索(废话)
其实更有意思的是(x=frac {n+1}2),由于(n)为奇数,则(2x=n+1),画个图可以发现在这个棋盘里任意选两个(x imes x)的子矩形一定会有相交
画个图发现把子矩形移到四个角后会夹出来一个十字(就是第(x)行与第(x)列),考虑这个十字有特殊的意义
然后经过缜da密dan思cai考xiang发现这个十字是有对称的意义在里头的,也就是说(a[1][i],a[1][x],a[1][i+x])的选择状态是有关联的,关联就是他们的选择状态的异或和为(0)(如果把选择设为(1),不选设为(0))。证明很简单,就是在矩形中画任意一个子矩形都只能将这三点中的两个或零个点包到矩形里头去,所以最终异或起来只能为(0)(即只需要知道这三个元素中的两个就能得到第三个)
类似的,发现除了左右关联(以第(x)列为轴)外,上下也是有关联的(以第(x)行为轴),到此发现了这是一个以中心十字为轴的矩形!(设第(x)行为行轴,第(x)列为列轴)
接下来就简单多了,由于(nleq 33)的范围使得不能枚举所有点,那可以枚举行轴(而行轴又是以列轴为轴的,所以只需要枚举行轴的前(x)个元素即可推出整个行轴),然后枚举列轴的情况(同样只需要枚举前(x)个元素),发现如果行轴情况确定的情况下,列轴的(x)个元素带来的影响(即每一行的贡献)都是相互独立的(即不需要(2^x)去枚举),每个格子带来的影响也是独立的
上面可能讲得有点抽象,下面细化下过程 (还不懂就看代码吧)
- 枚举第(x)行前(x)个格子选与不选 (O(2^x))
- 得出第(x)行整行的情况 (O(n))
- 枚举第(i)行第(x)个格子选与不选 (O(n))
- 由当前枚举状态枚举第(i)行每个格子((i,j)) (O(n))
- 推出格子((i,j),(i+x,j),(i,j+x),(i+x,j+x)) (O(1))
总复杂度为(O(2^xx^2)),把(x=17)代进去大概为(4e7)
Code
#include <cstdio>
const int N=34;
int a[N][N],rw[N],ln[N];
int n,t;
inline int calc(int i,int j){
int res=a[i][j],e;
e=a[i+t][j],res+=(rw[j]?-e:e);
e=a[i][j+t],res+=(ln[i]?-e:e);
e=a[i+t][j+t],res+=(ln[i]^rw[j+t]?-e:e);
return res>0?res:-res;
}
int main(){
scanf("%d",&n);t=n+1>>1;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
scanf("%d",&a[i][j]);
int ans=0;
for(int lim=1<<t,S=0;S<lim;++S){
int sum=0;
for(int i=1;i<=t;++i)
if(S&(1<<i-1))rw[i]=1,sum-=a[t][i];
else rw[i]=0,sum+=a[t][i];
for(int i=t+1;i<=n;++i){
rw[i]=rw[t]^rw[i-t];
if(rw[i])sum-=a[t][i];
else sum+=a[t][i];
}
for(int i=1,r0,r1;i<t;++i){
r0=a[i][t]+(rw[t]?-a[i+t][t]:a[i+t][t]);
ln[i]=0;for(int j=1;j<t;++j)r0+=calc(i,j);
r1=-a[i][t]+(rw[t]?a[i+t][t]:-a[i+t][t]);
ln[i]=1;for(int j=1;j<t;++j)r1+=calc(i,j);
sum+=r0>r1?r0:r1;
}ans=ans>sum?ans:sum;
}printf("%d
",ans);
return 0;
}