【做题记录】csp2025-搜索,折半搜索专题
A. 「NOIP2009」靶形数独
暴搜。
本着搜索必剪枝的思想,略微做一点优化:优先搜索 \(0\) 少的行。
然后就搜就行。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
namespace IO{
const int sz=1<<20;
char in[sz],*p1=in,*p2=in;
#define getchar() (p1==p2&&(p2=(p1=in)+fread(in,1,sz,stdin),p1==p2)?EOF:*p1++)
template<typename T=int>il int read(){
char ch=getchar();
while(ch<'0'||ch>'9'){
ch=getchar();
}
T x=0;
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x;
}
#undef getchar
char out[1<<20],*p3=out,s[50];
il int flush(){
fwrite(out,1,p3-out,stdout);
p3=out;
return 0;
}
#define putchar(ch) (p3==out+sz&&flush(),*p3++=(ch))
template<typename T>il void write(T x,bool typ=1){
int top=0;
if(x<0){
putchar('-');
x=-x;
}
do{
s[++top]=x%10|48;
x/=10;
}while(x);
while(top){
putchar(s[top--]);
}
putchar(typ?'\n':' ');
}
#undef putchar
}
using IO::read;
using IO::write;
const int hao[15][15]={{},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9}};
const int sco[15][15]={{},
{0,6,6,6,6,6,6,6,6,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,9,10,9,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,6,6,6,6,6,6,6,6}};
int a[15][15],ans=-1;
int p[15],cnt[15],b[105];
bool vis[3][15][15];
il int cal(){
int res=0;
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
res+=a[i][j]*sco[i][j];
}
}
return res;
}
il void dfs(int u){
if(u==82){
ans=max(ans,cal());
return ;
}
int x=b[u]/10,y=b[u]%10;
// cout<<x<<" "<<y<<"\n";
if(a[x][y]){
dfs(u+1);
return ;
}
for(int i=1;i<=9;i++){
if(!vis[0][x][i]&&!vis[1][y][i]&&!vis[2][hao[x][y]][i]){
a[x][y]=i;
vis[0][x][i]=1;
vis[1][y][i]=1;
vis[2][hao[x][y]][i]=1;
dfs(u+1);
a[x][y]=0;
vis[0][x][i]=0;
vis[1][y][i]=0;
vis[2][hao[x][y]][i]=0;
}
}
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
for(int i=1;i<=9;i++){
p[i]=i;
for(int j=1;j<=9;j++){
a[i][j]=read();
if(a[i][j]){
vis[0][i][a[i][j]]=1;
vis[1][j][a[i][j]]=1;
vis[2][hao[i][j]][a[i][j]]=1;
}
else{
cnt[i]++;
}
}
}
sort(p+1,p+10,[](const int &x,const int &y){return cnt[x]<cnt[y];});
for(int i=1,x,tot=0;i<=9;i++){
x=p[i];
for(int y=1;y<=9;y++){
b[++tot]=x*10+y;
}
}
// for(int i=1;i<=81;i++){
// cout<<b[i]<<" ";
// }
// puts("");
dfs(1);
write(ans);
IO::flush();
return 0;
}
}
int main(){return asbt::main();}
B. [CQOI2013] 新数独
还是暴搜。这些搜索题就别指望证明时间复杂度了,能过就行。
用 vector 存比自己大的和比自己小的,直接搜索过程中判合法,搜完了就输出就行。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
namespace IO{
const int bufsz=1<<20;
char obuf[bufsz],*p3=obuf,s[50];
il int flush(){
fwrite(obuf,1,p3-obuf,stdout);
p3=obuf;
return 0;
}
#define putchar(ch) (p3==obuf+bufsz&&flush(),*p3++=ch)
template<typename T>il void write(T x,bool typ=1){
int top=0;
do{
s[++top]=x%10|48;
x/=10;
}while(x);
while(top){
putchar(s[top--]);
}
putchar(typ?'\n':' ');
}
#undef putchar
}
using IO::write;
const int ge[15][15]={{},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9}};
int hao[15][15],ans[15][15];
vector<int> da[105],xo[105];
bool vis[3][15][15];
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
hao[i][j]=i*10+j;
}
}
for(int i=1,cnt1=0;i<=15;i++){
if(i==1||i==3||i==5||i==6||i==8||i==10||i==11||i==13||i==15){
cnt1++;
for(int j=1,u,v,cnt2=0;j<=6;j++){
cnt2++;
if(cnt2%3==0){
cnt2++;
}
char ch;
scanf(" %c",&ch);
u=hao[cnt1][cnt2],v=hao[cnt1][cnt2+1];
if(ch=='<'){
da[u].pb(v),xo[v].pb(u);
}
else{
xo[u].pb(v),da[v].pb(u);
}
}
}
else{
for(int j=1,u,v;j<=9;j++){
char ch;
scanf(" %c",&ch);
u=hao[cnt1][j],v=hao[cnt1+1][j];
if(ch=='^'){
da[u].pb(v),xo[v].pb(u);
}
else{
da[v].pb(u),xo[u].pb(v);
}
}
}
}
function<void(int,int)> dfs=[&](int x,int y){
if(x>9){
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
write(ans[i][j],j==9);
}
}
IO::flush();
exit(0);
}
int nx=x,ny=y+1;
if(y==9){
nx++,ny=1;
}
for(int i=1;i<=9;i++){
if(!vis[0][x][i]&&!vis[1][y][i]&&!vis[2][ge[x][y]][i]){
for(int u:da[hao[x][y]]){
int tx=u/10,ty=u%10;
if(!ans[tx][ty]){
continue;
}
if(ans[tx][ty]<i){
goto togo;
}
}
for(int u:xo[hao[x][y]]){
int tx=u/10,ty=u%10;
if(!ans[tx][ty]){
continue;
}
if(ans[tx][ty]>i){
goto togo;
}
}
ans[x][y]=i;
vis[0][x][i]=1;
vis[1][y][i]=1;
vis[2][ge[x][y]][i]=1;
dfs(nx,ny);
ans[x][y]=0;
vis[0][x][i]=0;
vis[1][y][i]=0;
vis[2][ge[x][y]][i]=0;
togo:;
}
}
};
dfs(1,1);
return 0;
}
}
int main(){return asbt::main();}
C. Anya and Cubes
折半搜索,先搜前一半,将结果按用的 \(!\) 数存下来并排序,搜后一半时枚举 \(!\) 数量,二分即可。
时间复杂度为 \(O(3^{\frac{n}{2}}m\log3^{\frac{n}{2}})\)。
Code>
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define lwrb lower_bound
#define uprb upper_bound
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
namespace IO{
const int bufsz=1<<20;
char ibuf[bufsz],*p1=ibuf,*p2=ibuf;
#define getchar() (p1==p2&&(p2=(p1=ibuf)+fread(ibuf,1,bufsz,stdin),p1==p2)?EOF:*p1++)
template<typename T=int>il T read(){
char ch=getchar();
while(ch<'0'||ch>'9'){
ch=getchar();
}
T x=0;
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x;
}
#undef getchar
char obuf[bufsz],*p3=obuf,s[50];
il int flush(){
fwrite(obuf,1,p3-obuf,stdout);
p3=obuf;
return 0;
}
#define putchar(ch) (p3==obuf+bufsz&&flush(),*p3++=(ch))
template<typename T>il void write(T x,bool typ=1){
int top=0;
do{
s[++top]=x%10|48;
x/=10;
}while(x);
while(top){
putchar(s[top--]);
}
}
#undef putchar
}
using IO::read;
using IO::write;
int n,m;
ll tar,a[35],fac[25],ans;
vector<ll> arr[35];
il void dfs1(int x,int y,ll sum){
if(x>n>>1){
arr[y].pb(sum);
return ;
}
dfs1(x+1,y,sum);
dfs1(x+1,y,sum+a[x]);
if(a[x]<=18&&fac[a[x]]<=tar&&y<m){
dfs1(x+1,y+1,sum+fac[a[x]]);
}
}
il void dfs2(int x,int y,ll sum){
if(x>n){
for(int i=0;i<=m-y;i++){
ans+=uprb(arr[i].begin(),arr[i].end(),tar-sum)-lwrb(arr[i].begin(),arr[i].end(),tar-sum);
}
return ;
}
dfs2(x+1,y,sum);
dfs2(x+1,y,sum+a[x]);
if(a[x]<=18&&fac[a[x]]<=tar&&y<m){
dfs2(x+1,y+1,sum+fac[a[x]]);
}
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
n=read(),m=read(),tar=read<ll>();
// cout<<n<<" "<<m<<" "<<tar<<"\n";
for(int i=1;i<=n;i++){
a[i]=read();
}
fac[0]=1;
for(int i=1;i<=18;i++){
fac[i]=fac[i-1]*i;
}
dfs1(1,0,0);
for(int i=0;i<=m;i++){
sort(arr[i].begin(),arr[i].end());
// cout<<i<<" "<<arr[i].size()<<"\n";
// for(int x:arr[i]){
// cout<<x<<" ";
// }
// puts("");
}
dfs2((n>>1)+1,0,0);
write(ans);
IO::flush();
return 0;
}
}
int main(){return asbt::main();}
/*
4 2 6227020842
17 15 13 10
*/
D. Two Fairs
其中 \(cnta\) 表示只和 \(a\) 相连的点数,\(cntb\) 同理。
使用并查集,首先将不包含 \(a\),\(b\) 的边加入,然后对每个连通块进行判断,简单计算贡献即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pii pair<int,int>
#define mp make_pair
#define fir first
#define sec second
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
namespace IO{
const int bufsz=1<<20;
char ibuf[bufsz],*p1=ibuf,*p2=ibuf;
#define getchar() (p1==p2&&(p2=(p1=ibuf)+fread(ibuf,1,bufsz,stdin),p1==p2)?EOF:*p1++)
template<typename T=int>il T read(){
char ch=getchar();
while(ch<'0'||ch>'9'){
ch=getchar();
}
T x=0;
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x;
}
#undef getchar
char obuf[bufsz],*p3=obuf,s[50];
il int flush(){
fwrite(obuf,1,p3-obuf,stdout);
p3=obuf;
return 0;
}
#define putchar(ch) (p3==obuf+bufsz&&flush(),*p3++=(ch))
template<typename T>il void write(T x,bool typ=1){
int top=0;
do{
s[++top]=x%10|48;
x/=10;
}while(x);
while(top){
putchar(s[top--]);
}
putchar(typ?'\n':' ');
}
#undef putchar
}
using IO::read;
using IO::write;
const int maxn=2e5+5,maxm=5e5+5;
int T,n,m,a,b,fa[maxn],sz[maxn];
pii e[maxm];
bool ina[maxn],inb[maxn];
il int find(int x){
return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void merge(int u,int v){
u=find(u),v=find(v);
if(u==v){
return ;
}
if(sz[u]>sz[v]){
sz[u]+=sz[v],fa[v]=u;
}
else{
sz[v]+=sz[u],fa[u]=v;
}
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
T=read();
while(T--){
n=read(),m=read(),a=read(),b=read();
for(int i=1;i<=n;i++){
fa[i]=i,sz[i]=1;
}
for(int i=1;i<=m;i++){
e[i]=mp(read(),read());
if(e[i].fir!=a&&e[i].fir!=b&&e[i].sec!=a&&e[i].sec!=b){
merge(e[i].fir,e[i].sec);
}
}
for(int i=1;i<=m;i++){
if(e[i].fir==a){
ina[find(e[i].sec)]=1;
}
if(e[i].sec==a){
ina[find(e[i].fir)]=1;
}
if(e[i].fir==b){
inb[find(e[i].sec)]=1;
}
if(e[i].sec==b){
inb[find(e[i].fir)]=1;
}
}
int cnta=0,cntb=0;
for(int i=1;i<=n;i++){
if(find(i)!=i||i==a||i==b){
continue;
}
if(ina[i]&&inb[i]){
continue;
}
if(ina[i]){
cnta+=sz[i];
}
if(inb[i]){
cntb+=sz[i];
}
}
write(cnta*1ll*cntb);
for(int i=1;i<=n;i++){
ina[i]=inb[i]=0;
}
}
IO::flush();
return 0;
}
}
int main(){return asbt::main();}
E. Expression
从低位向高位搜索,若这一位符合要求则去搜下一位,否则就在 \(a\),\(b\),\(c\) 中选一个在这一位补数字。特别地,如果搜索时发现 \(c=0\) 了,那就直接把 \(c\) 的高位补成 \(a+b+\texttt{进位}\) 就行。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
namespace IO{
const int bufsz=1<<20;
char ibuf[bufsz],*p1=ibuf,*p2=ibuf;
#define getchar() (p1==p2&&(p2=(p1=ibuf)+fread(ibuf,1,bufsz,stdin),p1==p2)?EOF:*p1++)
template<typename T=int>il T read(){
char ch=getchar();
while(ch<'0'||ch>'9'){
ch=getchar();
}
T x=0;
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x;
}
#undef getchar
char obuf[bufsz],*p3=obuf,s[50];
il int flush(){
fwrite(obuf,1,p3-obuf,stdout);
p3=obuf;
return 0;
}
#define putchar(ch) (p3==obuf+bufsz&&flush(),*p3++=(ch))
template<typename T>il void write(T x,char typ='\n'){
int top=0;
do{
s[++top]=x%10|48;
x/=10;
}while(x);
while(top){
putchar(s[top--]);
}
if(~typ){
putchar(typ);
}
}
#undef putchar
}
using IO::read;
using IO::write;
const int inf=0x3f3f3f3f;
int ans=inf;
ll pw10[25],ansa,ansb,ansc;
il void dfs(ll a,ll b,ll c,ll jw,ll na,ll nb,ll nc,int cur,int dep){
// cout<<a<<" "<<b<<" "<<c<<" "<<jw<<" "<<na<<" "<<nb<<" "<<nc<<" "<<cur<<" "<<dep<<"\n";
if(cur>=ans){
return ;
}
if(!a&&!b&&!c&&!jw){
ansa=na,ansb=nb,ansc=nc,ans=cur;
return ;
}
if(!c){
ll tmp=a+b+jw;
int len=0;
do{
len++,tmp/=10;
}while(tmp);
dfs(0,0,0,0,na+a*pw10[dep],nb+b*pw10[dep],nc+(a+b+jw)*pw10[dep],cur+len,dep);
return ;
}
ll ta=a%10,tb=b%10,tc=c%10;
if((ta+tb+jw)%10==tc){
dfs(a/10,b/10,c/10,(ta+tb+jw)/10,na+ta*pw10[dep],nb+tb*pw10[dep],nc+tc*pw10[dep],cur,dep+1);
return ;
}
dfs(a*10+(tc-tb-jw+100)%10,b,c,jw,na,nb,nc,cur+1,dep);
dfs(a,b*10+(tc-ta-jw+100)%10,c,jw,na,nb,nc,cur+1,dep);
dfs(a,b,c*10+(ta+tb+jw)%10,jw,na,nb,nc,cur+1,dep);
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
pw10[0]=1;
for(int i=1;i<=18;i++){
pw10[i]=pw10[i-1]*10;
}
ll a=read(),b=read(),c=read();
dfs(a,b,c,0,0,0,0,0,0);
write(ansa,'+'),write(ansb,'='),write(ansc);
IO::flush();
return 0;
}
}
int main(){return asbt::main();}
F. Distinct Paths
暴搜 + 剪枝。一行一行搜索,对于一个格子,它的左上方出现过的颜色就都不能出现了。
考虑剪枝。首先是一个比较好想的剪枝:对于本来没在矩阵中出现过的颜色,如果它是第一次被填入当前格,那此时这些颜色是等价的。因此只用跑一次,枚举其它颜色时直接加上之前的结果即可。
然后是可行性剪枝,即当前格到右下角的格子数量 \(>\) 可选的颜色数量,那直接无解。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define popcnt __builtin_popcount
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
namespace IO{
const int bufsz=1<<20;
char ibuf[bufsz],*p1=ibuf,*p2=ibuf;
#define getchar() (p1==p2&&(p2=(p1=ibuf)+fread(ibuf,1,bufsz,stdin),p1==p2)?EOF:*p1++)
template<typename T=int>il T read(){
char ch=getchar();
while(ch<'0'||ch>'9'){
ch=getchar();
}
T x=0;
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x;
}
#undef getchar
char obuf[bufsz],*p3=obuf,s[50];
il int flush(){
fwrite(obuf,1,p3-obuf,stdout);
p3=obuf;
return 0;
}
#define putchar(ch) (p3==obuf+bufsz&&flush(),*p3++=(ch))
template<typename T>il void write(T x,bool typ=1){
int top=0;
do{
s[++top]=x%10|48;
x/=10;
}while(x);
while(top){
putchar(s[top--]);
}
putchar(typ?'\n':' ');
}
#undef putchar
}
using IO::read;
using IO::write;
const int mod=1e9+7;
int n,m,tot,a[15][15],num[15],sta[15][15];
il int dfs(int x,int y){
if(y>m){
x++,y=1;
}
if(x>n){
return 1;
}
int S=sta[x][y-1]|sta[x-1][y],res=0,tmp=-1;
if(n+m-x-y+1>tot-popcnt(S)){
return 0;
}
for(int i=1;i<=tot;i++){
if((S>>(i-1)&1)||a[x][y]&&a[x][y]!=i){
continue;
}
sta[x][y]=S|1<<(i-1);
num[i]++;
if(num[i]==1){
if(tmp==-1){
tmp=dfs(x,y+1);
}
(res+=tmp)%=mod;
}
else{
(res+=dfs(x,y+1))%=mod;
}
num[i]--;
}
return res;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
n=read(),m=read(),tot=read();
if(n+m>tot+1){
puts("0");
return 0;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
a[i][j]=read();
num[a[i][j]]++;
}
}
write(dfs(1,1));
IO::flush();
return 0;
}
}
int main(){return asbt::main();}
G. Bear and Square Grid
考虑枚举 \(m\times m\) 的矩形的位置并计算答案。
答案可以分为框内 X 的数量 \(+\) 框内完整的 . 连通块的数量 \(+\) 在框外与框的边相邻的 . 连通块的大小之和。分别求解。
第一部分,显然对 X 的数量做二维前缀和即可。第三部分,\(O(m)\) 枚举四周的连通块即可。
对于第二部分,我们记录每个连通块最左的一列 \(ly\),最右的一列 \(ry\),最上的一行 \(lx\),和最下的一行 \(rx\)。如果连通块的长度或宽度超过了 \(m\),那它不可能完全被包含在一个方框中。否则能完全包含它的方框是确定的,它们的左上角为 \(\{(x,y)\mid x\in[rx-m+1,lx],y\in[ry-m+1,ly]\}\)。二维差分即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
char ch;\
int fu=1;\
while(!isdigit(ch=getchar()))\
fu-=(ch=='-')<<1;\
x=ch&15;\
while(isdigit(ch=getchar()))\
x=(x<<1)+(x<<3)+(ch&15);\
x*=fu;\
}
#define pii pair<int,int>
#define mp make_pair
#define fir first
#define sec second
#define pb push_back
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=505,maxm=maxn*maxn;
const int inf=0x3f3f3f3f;
int n,m,num1[maxn][maxn];
int hao[maxn][maxn],tot;
int fa[maxm],sz[maxm];
int lx[maxm],rx[maxm];
int ly[maxm],ry[maxm];
int num2[maxn][maxn];
pii p[maxm];
bool vis[maxm];
vector<int> usd;
char s[maxn][maxn];
il int find(int x){
return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void merge(int u,int v){
u=find(u),v=find(v);
if(u==v){
return ;
}
if(sz[u]>sz[v]){
sz[u]+=sz[v],fa[v]=u;
}
else{
sz[v]+=sz[u],fa[u]=v;
}
}
il void add(int x1,int y1,int x2,int y2,int val){
num2[x1][y1]+=val;
num2[x2+1][y1]-=val;
num2[x1][y2+1]-=val;
num2[x2+1][y2+1]+=val;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
read(n)read(m);
for(int i=1;i<=n;i++){
scanf(" %s",s[i]+1);
for(int j=1;j<=n;j++){
num1[i][j]=num1[i-1][j]+num1[i][j-1]-num1[i-1][j-1];
if(s[i][j]=='X'){
num1[i][j]++;
}
if(s[i][j]=='.'){
hao[i][j]=++tot;
p[tot]=mp(i,j);
}
}
}
for(int i=1;i<=tot;i++){
fa[i]=i,sz[i]=1;
lx[i]=ly[i]=inf;
rx[i]=ry[i]=-inf;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(s[i][j]=='.'){
if(s[i][j+1]=='.'){
merge(hao[i][j],hao[i][j+1]);
}
if(s[i+1][j]=='.'){
merge(hao[i][j],hao[i+1][j]);
}
}
}
}
for(int i=1,u;i<=tot;i++){
u=find(i);
lx[u]=min(lx[u],p[i].fir);
rx[u]=max(rx[u],p[i].fir);
ly[u]=min(ly[u],p[i].sec);
ry[u]=max(ry[u],p[i].sec);
}
for(int i=1;i<=tot;i++){
if(find(i)!=i){
continue;
}
// cout<<p[i].fir<<" "<<p[i].sec<<"\n";
// cout<<lx[i]<<" "<<rx[i]<<" "<<ly[i]<<" "<<ry[i]<<"\n";
if(rx[i]-lx[i]+1>m||ry[i]-ly[i]+1>m){
continue;
}
add(max(rx[i]-m+1,1),max(ry[i]-m+1,1),lx[i],ly[i],sz[i]);
}
// puts("----------");
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
num2[i][j]+=num2[i-1][j]+num2[i][j-1]-num2[i-1][j-1];
}
}
int ans=0;
for(int x1=1,x2=m;x2<=n;x1++,x2++){
for(int y1=1,y2=m,res;y2<=n;y1++,y2++){
res=num1[x2][y2]-num1[x1-1][y2]-num1[x2][y1-1]+num1[x1-1][y1-1]+num2[x1][y1];
// cout<<x1<<" "<<y1<<"\n";
// cout<<res<<"\n";
for(int i=y1,u;i<=y2;i++){
if(hao[x1-1][i]){
u=find(hao[x1-1][i]);
if(!vis[u]){
vis[u]=1;
res+=sz[u];
usd.pb(u);
}
}
if(hao[x2+1][i]){
u=find(hao[x2+1][i]);
if(!vis[u]){
vis[u]=1;
res+=sz[u];
usd.pb(u);
}
}
}
for(int i=x1,u;i<=x2;i++){
if(hao[i][y1-1]){
u=find(hao[i][y1-1]);
if(!vis[u]){
vis[u]=1;
res+=sz[u];
usd.pb(u);
}
}
if(hao[i][y2+1]){
u=find(hao[i][y2+1]);
if(!vis[u]){
vis[u]=1;
res+=sz[u];
usd.pb(u);
}
}
}
// cout<<x1<<" "<<y1<<" "<<res<<"\n";
ans=max(ans,res);
for(int i:usd){
vis[i]=0;
}
usd.clear();
}
}
printf("%d",ans);
return 0;
}
}
int main(){return asbt::main();}
I. Lizard Era: Beginning
直接折半搜。
推式子:
用 multimap 存即可。需要三进制状压。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define read(x){\
char ch;\
int fu=1;\
while(!isdigit(ch=getchar()))\
fu-=(ch=='-')<<1;\
x=ch&15;\
while(isdigit(ch=getchar()))\
x=(x<<1)+(x<<3)+(ch&15);\
x*=fu;\
}
#define pii pair<int,int>
#define mp make_pair
#define fir first
#define sec second
#define lwrb lower_bound
#define uprb upper_bound
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int inf=0x3f3f3f3f;
int n,a[30],b[30],c[30];
int pw3[20],ans=-inf;
pii Ans;
multimap<pii,pii> hp;
il void dfs1(int x,int ra,int rb,int rc,int sta){
if(x>n>>1){
hp.insert(mp(mp(ra-rb,rb-rc),mp(ra,sta)));
return ;
}
dfs1(x+1,ra+a[x],rb+b[x],rc,sta+2*pw3[x-1]);
dfs1(x+1,ra+a[x],rb,rc+c[x],sta+pw3[x-1]);
dfs1(x+1,ra,rb+b[x],rc+c[x],sta);
}
il void dfs2(int x,int ra,int rb,int rc,int sta){
if(x>n){
auto l=hp.lwrb(mp(rb-ra,rc-rb)),r=hp.uprb(mp(rb-ra,rc-rb));
for(int tmp;l!=r;l++){
tmp=ra+l->sec.fir;
if(ans<tmp){
ans=tmp;
Ans=mp(l->sec.sec,sta);
}
}
return ;
}
dfs2(x+1,ra+a[x],rb+b[x],rc,sta+2*pw3[x-(n>>1)-1]);
dfs2(x+1,ra+a[x],rb,rc+c[x],sta+pw3[x-(n>>1)-1]);
dfs2(x+1,ra,rb+b[x],rc+c[x],sta);
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
read(n);
for(int i=1;i<=n;i++){
read(a[i])read(b[i])read(c[i]);
}
pw3[0]=1;
for(int i=1;i<=15;i++){
pw3[i]=pw3[i-1]*3;
}
dfs1(1,0,0,0,0);
dfs2((n>>1)+1,0,0,0,0);
if(ans<=-inf){
puts("Impossible");
return 0;
}
// cout<<ans<<"\n";
for(int i=1;i<=n>>1;i++){
switch(Ans.fir/pw3[i-1]%3){
case 0:{
puts("MW");
break;
}
case 1:{
puts("LW");
break;
}
default:{
puts("LM");
break;
}
}
}
for(int i=n>>1;i<n;i++){
switch(Ans.sec/pw3[i-(n>>1)]%3){
case 0:{
puts("MW");
break;
}
case 1:{
puts("LW");
break;
}
default:{
puts("LM");
break;
}
}
}
return 0;
}
}
int main(){return asbt::main();}

浙公网安备 33010602011771号