迎面走来的是邪恶构造题
记录一些构造题,也许不会太频繁更新(谁没事做这种神秘题)
题目让你构造一个类似和的积等于积的和,然后和不能为 \(0\),\(a_i\) 也不能为 \(0\)。
考虑和的积肯定不能太大,也就是相差大于 \(1\) 的肯定没几个。
考虑一组最小的组合是 \(2,-1\)。
嗯,然后你惊人的发现一组合法的形如 \(-n,2,-1,...,2,-1,4\),嗯,就这么简单。
还有一些构造方式我也没去看了,这里再给一组:\(2,-1,2,-1...,2,-1,2n,-1\)。
有意义一点的构造,如果是作为一道考试题的话,应该会有一档全是 # 的档,读者可以先考虑这个特殊情况会是什么样子。
我们钦定 # 为陆地,. 为障碍,那么如果棋盘无限大且全是陆地的话,我们是可以通过构造使得每一个十字架(不妨这么称呼)都恰好覆盖 \(5\) 个陆地,那么一定是可行的,然后可以给十字架染色,注意到只需要两种颜色就可以区分,如下图所示:

你从里面截取一个大小为 \(n\times m\) 的矩形,会发现恰好有五种不同的取法。
然后考虑有障碍的时候,比如如果 \((i,j)\) 是障碍,那么有一种情况这里就放不了了,我们直接放相邻的不是障碍的点即可(注意到这样一定时候可以的),然后放置每多一次,说明这里有一条障碍和陆地的连边(姑且这么说)。
你惊奇的发现,如果把这五种放置点的数量加起来,恰好是 \(r+c\) 个位置!
然后,小学生都知道(也许吧)\(a_1+a_2+a_3+a_4+a_5 = r+c\),那么 \(\min a_i \le \frac{r+c}{5}\),所以对五种方案取 \(\min\) 输出即可。
关于 vector:建议少开,然后 resize 预留空间,避免空间超了。
code
code
#include<bits/stdc++.h>
using namespace std;
namespace IO
{
template<typename T>
void read(T &_x){_x=0;int _f=1;char ch=getchar();while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();_x*=_f;}
template<typename T,typename... Args>
void read(T &_x,Args&...others){Read(_x);Read(others...);}
const int BUF=20000000;char buf[BUF],to,stk[32];int plen;
#define pc(x) buf[plen++]=x
#define flush(); fwrite(buf,1,plen,stdout),plen=0;
template<typename T>inline void print(T x){if(!x){pc(48);return;}if(x<0) x=-x,pc('-');for(;x;x/=10) stk[++to]=48+x%10;while(to) pc(stk[to--]);}
}
using namespace IO;
const int N = 2e6+10,M = 6e6;
int t,n,m,mx,x,y,cnt;
bool v[M],v2[M],v3[M];
char c;
signed main()
{
// freopen("indian.in","r",stdin);
// freopen("indian.out","w",stdout);
read(t);
while(t--)
{
cnt = 0;
read(n),read(m);
vector<vector<int>>id(n+2,vector<int>(m+2,0));
for(int i = 0;i <= n+1;i++)
for(int j = 0;j <= m+1;j++)
id[i][j] = ++cnt;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
cin >> c;
if(c == '#') v[id[i][j]] = 1;
else v[id[i][j]] = 0;
}
} mx = 1e14;
for(int i = 0;i <= 4;i++)
{
x = 0;
for(int j = 0;j <= n+1;j++)
for(int z = 0;z <= m+1;z++) v3[id[j][z]] = 0;
for(int j = 1;j <= n;j++)
for(int z = 1;z <= m;z++)
{//手玩可以发现(2*j+z)%5是同类点
if(v[id[j][z]] == 1 && (2*j+z)%5==i && v3[id[j-1][z]] != 1 && v3[id[j+1][z]] != 1 && v3[id[j][z-1]] != 1 && v3[id[j][z+1]] != 1)
v3[id[j][z]] = 1,x++;
}
for(int j = 1;j <= n;j++)
for(int z = 1;z <= m;z++)
if(v3[id[j][z]] == 0 && v[id[j][z]] == 1 && v3[id[j-1][z]] != 1 && v3[id[j+1][z]] != 1 && v3[id[j][z-1]] != 1 && v3[id[j][z+1]] != 1)
v3[id[j][z]] = 1,x++;
if(x < mx)
{
mx = x;
for(int j = 1;j <= n;j++)
for(int z = 1;z <= m;z++)
v2[id[j][z]] = v3[id[j][z]];
}
}
for(int j = 1;j <= n;j++,pc('\n'))
for(int z = 1;z <= m;z++)
if(v[id[j][z]] == 0) pc('.');
else if(v2[id[j][z]]) pc('S');
else pc('#');
flush();
}
return 0;
}
/*
无敌了,神秘构造题
先考虑全是陆地的情况,且仅考虑S
考虑无限棋盘的时候,我们肯定会考虑每一个对应5个
然后就是若干十字架
那么对于有限制的,直接对于五种方式选一个最小的即可
即十字架的上下左右或中间
对于有海洋的,可能会因为不行而放四边
每放一次,就说明有周长
那么5种情况,总数一定是S+C
那么总有一个合法
*/
浙公网安备 33010602011771号