云斗杯 · 五月 Silver 组模拟赛T1T2题解

为啥只有T1和T2? 因为T3T4不会

T1:无所谓的第一道题

题意:给定一张 n×n01 方阵,请计算其中 X 的数目。

X 定义为用 1 填充且形状为 X 的联通块。具体的,X 由左向斜线 \ 和右向斜线 / 构成,且需要保证左向斜线和右向斜线长度相等,而且 X 是中心对称图形,斜线长度大于 1

例如:

101
010
101

中有一个X;

1001
0110
0110
1001

中有两个斜线长度为4的X。

————————↑以上内容均为题目描述↑—————————

考虑如何做:对于中心点为 11 的X,我们可以很容易地想到去枚举中心点,然后向外扩。

那么对于中心为一个块的怎么处理呢?

可以回想一下我们学manachar的时候,是在字符串中插入一些相同的占位符号,以保证每次都能找到一个对称中心。这道题我们也可以用相同的思路处理。也就是,在每一行和每一列之间插入一个字符,然后枚举中心点即可。

复杂度:O(8n3)

当然,可以优化的点很多。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 205;
char a[N][N], s[N][N];
int n;
int dx[4] = {-1, 1, -1, 1};
int dy[4] = {-1, 1, 1, -1};
int X[4], Y[4];
int main() {
scanf("%d", &n);
for(int i = 1; i<=n; i++) {
scanf("%s", a[i]+1);
}
for(int i = 1; i<=n; i++) {
for(int j = 1; j<=n; j++) {
s[(i<<1)-1][(j<<1)-1] = a[i][j];
s[(i<<1)-1][j<<1] = '$';
}
for(int j = 1; j<=2*n-1; j++) {
s[i<<1][j] = '$';
}
}
n<<=1;
for(int i = 0; i<=n; i++) {
s[0][i] = s[n][i] = '#';
s[i][0] = s[i][n] = '#';//边界标志。
}
n--;
long long ans = 0;
for(int i = 1; i<=n; i++) {
for(int j = 1; j<=n; j++) {
for(int k = 1; k<=n; k++) {
if(s[i][j] == '0') {
continue;
}//注意:中间点为0的时候不能扩展qwq否则只有20分
X[0] = i+k*dx[0], Y[0] = j+k*dy[0];
X[1] = i+k*dx[1], Y[1] = j+k*dy[1];
X[2] = i+k*dx[2], Y[2] = j+k*dy[2];
X[3] = i+k*dx[3], Y[3] = j+k*dy[3];
if((s[X[0]][Y[0]] == '#') || (s[X[1]][Y[1]] == '#') || (s[X[2]][Y[2]] == '#') || (s[X[3]][Y[3]] == '#')) {
break;
}
if((s[X[0]][Y[0]] == '0') || (s[X[1]][Y[1]] == '0') || (s[X[2]][Y[2]] == '0') || (s[X[3]][Y[3]] == '0')) {
break;
}
if((s[X[0]][Y[0]] == '1') && (s[X[1]][Y[1]] == '1') && (s[X[2]][Y[2]] == '1') && (s[X[3]][Y[3]] == '1')) {
ans++;
}
}
}
}
printf("%lld\n", ans);
return 0;
}

T2王座下的背叛纲领

这个题我最后一秒写完的,没交上……

简化题意:给定一个 w×h 的方阵,每次可以将一个点及其上下左右四个点异或 1,求将矩阵内元素全变成 1 的最少的操作次数。

说一说我拿到题的思路:我一开始没啥特别好的思路,但是看到 w 很小,似乎可以状压解决,而且这个题貌似很像之前做过的P3977棋盘。并且因为是异或,所以一个点改变两次状态是没有意义的。于是我就考虑状压dp。我先搜出来了一行的所有操作情况及其对应的状态改变,但是在转移的时候发现总是会把下一个状态算重。

这时候,我发现一个很重要的性质:就是,当你的上一行的状态确定时,你的下一行的操作情况也是确定的。因为你必须保证上一行全变成 1!那么,你的下一行所对应的操作就是去变换上一行状态中 0 所对应的位置。

很遗憾,发现这个性质后我没有好好利用,还是去想dp。最后才想到,其实对于第一行的每个初始状态,都有最多一个确定的方案去变换矩阵中的所有元素。那么,我们只需要枚举第一行的状态,然后往下搜索即可。注意,只有最后一行的终状态全为 1,我们才能统计答案。

Code:

#include<bits/stdc++.h>
using namespace std;
const int N = 1200, H = 1210;
int T;
int w, h;
int fst[H];
int tsf[N], aft[N], opr[N], tot;
//分别对应:变换方式(1为变换,0为不变),对应的最终结果(1为异或1,0为不异或),操作次数,方案总数。
int f_tsf[N];//某一状态对应的方案
void dfs(int pos, int x, int y, int op) {
if(pos>=w) {
tsf[++tot] = x;
aft[tot] = y;
opr[tot] = op;
f_tsf[x] = tot;
return;
}
dfs(pos+1, x, y, op);
if(w == 1) {
dfs(pos+1, (x|(1<<pos)), y^1, op+1);
} else if(pos<1) {
dfs(pos+1, (x|(1<<pos)), (y^3), op+1);
} else if(pos == w-1) {
dfs(pos+1, (x|(1<<pos)), (y^(3<<(pos-1))), op+1);
} else {
dfs(pos+1, (x|(1<<pos)), (y^(7<<(pos-1))), op+1);
}
}
int mx;
void print_fst(int aa[], int bb[]) {
for(int i = 1; i<=tot; i++, puts("")) {
for(int j = 0 ; j<w; j++) {
printf("%d", ((aa[i]>>j)&1));
}
printf(" ");
for(int j = 0; j<w; j++) {
printf("%d", ((bb[i]>>j)&1));
}
}
}
long long ans;
void init() {
ans = 0x3f3f3f3f3f3f3f3f;
memset(opr, 0x3f, sizeof(opr));
memset(fst, 0, sizeof(fst));
tot = 0;
memset(tsf, 0, sizeof(tsf));
memset(aft, 0, sizeof(aft));
}
void dfs2(int pos, int lst, long long sum, int opp) {
if(pos>h) {
if(lst == mx) ans = min(sum, ans);
return;
}
int ned = lst^mx;
int tmp = f_tsf[ned];
dfs2(pos+1, fst[pos]^tsf[opp]^aft[tmp], sum+opr[tmp], tmp);
}
int main() {
scanf("%d", &T);
while(T--) {
init();
scanf("%d%d", &w, &h);
for(int i = 0; i<w; i++) {
for(int j = 1; j<=h; j++) {
char tmp[2];
scanf("%s", tmp);
if(tmp[0] == 'A') {
fst[j]|=(1<<i);
}
}
}
mx = (1<<w)-1;
dfs(0, 0, 0, 0);
for(int i = 1; i<=tot; i++) {
dfs2(2, fst[1]^aft[i], opr[i], i);
}
printf("%lld\n", ans);
}
return 0;
}
posted @   霜木_Atomic  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示