Codeforces Round #843 (Div. 2) F. Laboratory on Pluto

题目链接

首先看问题一(算最小周长),并没有用题解的神奇结论,而是直接整除分块枚举(n1)/x,取对应的最小x,在n种可能内取最优的(能暴力算为什么要考虑结论呢)然而最后这部分被卡常了,,,整除分块换成枚举x才过,先总结一下这部分的常数问题
对于整除分块,如果对于一个x,需要知道能得到n/y=x的y的区间,就需要正常写;如果只需要左端点,那么根号范围内枚举较小数即可,n/i就是左端点,可以节省一些常数(大概是少了一个除法吧)。

需要左右端点

	int m=n-1,t=0,mn=n+n+2,ans=0;
	for(int i=1;i<=n;){
		int x=m/i,j;
		if(!x) j=n;
		else j=m/x;
		if(i+x+1>mn){
			i=j+1; 
			continue;
		}
		node u=(node){i,i+x+1};
		mn=u.s;
		p[++t]=u;
		i=j+1;
	}
	

只需要左端点

	int m=(int)sqrt(n)+1,t=0,mn=n+n+2,ans=0;
	for(int i=1;i<=m;i++){
		int x=(n-1)/i+1;
		//此题x和n/x是对称等价的,一般的题还要work(n/i)
		if(x<i) continue;
		if(i+x>mn) continue;
		mn=i+x;
		p[++t]=(node){i,i+x};
	}

(小问题搞了半天,这就是不想动脑的代价)

upd 需要左右端点的卡常做法

vp的CFER141E整除分块又被卡常了!(虽然这个题的正解是log的)但这个数据范围很难不想用暴力过,于是yy了一个大概常数/2的做法:

for(int k=1;k<=n;k++){
		//x>0
		//cout<<"k="<<k<<endl;
		int m=(int)(sqrt(a[k]-1)+eps),tp=0,tq=0;
		for(int i=1;i<=m;i++){
			int x=(a[k]-1)/i;
			p[++tp]=(node){i,x};
			if(i!=x) q[++tq]=(node){x,i};
		}
		while(tq) p[++tp]=q[tq--];
		p[tp+1].r=0;
		for(int i=1;i<=tp;i++){
			int l=p[i+1].r+1,r=min(p[i].r,(b[k]-1)/p[i].x);
			//cout<<p[i].x<<" "<<l<<" "<<r<<endl;
			if(l<=r) s[l]++,s[r+1]--;
		}
		//x=0
		s[a[k]]++;
	}

回到正题,考虑方案数,原本naive了以为每列的都填满或者只有一个为空,后来发现只要是一个凸多边形即可,然后感觉是个dp,但没想清楚就结束了。

对于这个凸多边形,直接dp有点麻烦;但发现本质上是在两个维度上是凸的,而凸本身又可以分为两个单调的部分,而这些问题都是相似的,即将凸多边形分为四个阶梯状;为了避免对凸多边形划分不同的重复计数,考虑空白的部分,就是四个角的阶梯状。

考虑如果已知四个角分别的面积,在保证它们不相交的情况下,直接将四个值乘起来即可;而如果相交,说明可以得到更小的周长,即不可能相交。所以在已知每个面积对应的阶梯方案数下,直接做一个背包即可。

考虑每个面积对应的阶梯方案数,这是经典的分拆数问题,考虑最小的数是否是1可以得到一个O(n2)的DP。在本题中,易证空白的面积是根号级别的(否则可以往较小的边缩),故可以通过。实际上,这个问题是五边形数对应多项式的逆,可以O(nlogn)求出:分拆数小结

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5,M=2005;
int P;
int f[M][M],g[5][M];
void init(int n){
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=n;j++){
			f[i][j]=f[i-1][j];
			if(i<=j) (f[i][j]+=f[i][j-i])%=P;
		}
	}
	g[0][0]=1;
	for(int i=1;i<=4;i++){
		for(int j=0;j<=n;j++){
			for(int k=0;k<=j;k++) (g[i][j]+=1ll*g[i-1][j-k]*f[n][k]%P)%=P;
		}
	}
	//cout<<g[4][16]<<endl;
}
int T,n,op;
int cnt(int x){
	return x+(n-1)/x+1;
}
struct node{
	int x,s;
}p[M];
int sm,mx;
void work(){
	cin>>n;
	//n=4e5;
	if(n==1){
		if(op==1){
			cout<<1<<" "<<1<<endl;
			puts("#");
		}
		else cout<<4<<" "<<1<<endl;
		return;
	}
	int m=(int)sqrt(n)+1,t=0,mn=n+n+2,ans=0;
	for(int i=1;i<=m;i++){
		int x=(n-1)/i+1;
		if(x<i) continue;
		//cout<<i<<" "<<j<<" "<<x<<endl;
		if(i+x>mn) continue;
		//cout<<i<<" "<<i+x+1<<" "<<j<<endl;
		mn=i+x;
		p[++t]=(node){i,i+x};
		//cout<<"ans="<<ans<<endl;
	}
	//cout<<"t="<<t<<endl;
	//sm+=t;
	//mx=max(mx,t);
//	return;
	for(int i=t;i && p[i].s==mn;i--){
		node u=p[i];
		int res=n-(u.s-u.x-1)*u.x;
		if(op==1){
			printf("%d %d\n",u.s-u.x,u.x);
			for(int j=1;j<=res;j++) putchar('#');
			for(int j=res+1;j<=u.x;j++) putchar('.');
			puts("");
			for(int i=2;i<=u.s-u.x;i++){
				for(int j=1;j<=u.x;j++) putchar('#');
				puts("");
			}
			return;
		}
		//cout<<"x="<<u.x<<" s="<<u.s<<endl;
		//cout<<"res="<<res<<" "<<u.x-res<<" "<<g[4][16]<<endl;
		ans+=g[4][u.x-res];
		if(ans>=P) ans-=P;
		if(u.x!=u.s-u.x){
			ans+=g[4][u.x-res];
			if(ans>=P) ans-=P;
		}
		//cout<<ans<<endl;
	}
	printf("%d %d\n",mn*2,ans);
}
int main()
{
	//srand(time(0));
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	cin>>T>>op;
	if(op==2) cin>>P,init(1000);
	while(T--) work();
	//cout<<sm<<" "<<mx<<endl;
	return 0;
}
posted @   sz[sz]  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示