【做题记录】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

\[ans=cnta\times cntb \]

其中 \(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
直接折半搜。
推式子:

\[\begin{aligned} &a_i+a_j=b_i+b_j=c_i+c_j\\ \Leftrightarrow&a_i-b_i=b_j-a_j\land b_i-c_i=c_j-b_j \end{aligned} \]

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();}
posted @ 2025-01-17 19:48  zhangxy__hp  阅读(44)  评论(0)    收藏  举报