CF2068D

思路

升序、 降序排序,对应位置乘起来。

如何判定一个 是否合法。构造一个有根树,每个点有长为 的两条出边,有 个叶子, 数组为叶子的深度。 中的 可以合并为 。即从大到小枚举值域 ,设有 ,加入 个深度为 的叶子,然后 ,有 。只要满足 即可。

所以可以 dp,再设一维 表示已经选了前 深的叶子,加入 个深度为 的叶子权值加 。对于一个 复杂度为 级别的,最大可以取到

还原方案即模拟构造树的过程。

code

int n,a[maxn],id[maxn];
char c[10];
int dp[21][maxn][maxn][maxn];
void chkmn(int &u,int v){(u>v)&&(u=v);}
pii fa[maxn*15];
int t[21],pos;
vector<char> ans[maxn];
void work(){
	n=read();
	for(int i=1;i<=n;i++)a[i]=read(),a[i]=read();
	for(int i=1;i<=n;i++)id[i]=i;
	sort(id+1,id+n+1,[&](int u,int v){return a[u]<a[v];});
	sort(a+1,a+n+1);
	mems(dp,0x3f);dp[20][0][0][0]=0;
	for(int x=20;x;x--){
		for(int i=0;i<n;i++){
			for(int j=0;j<=i;j++){
				for(int k=0;j+k<=i;k++){
					chkmn(dp[x][i+1][j][k+1],dp[x][i][j][k]+a[i+1]*x);
				}
			}
		}
		for(int i=0;i<=n;i++){
			for(int j=0;j<=i;j++){
				for(int k=j;j+k<=i;k++)if(dp[x][i][j][k]<inf){
					chkmn(dp[x-1][i][k-j][j],dp[x][i][j][k]);
				}
			}
		}
	}
	// printf("%lld\n",dp[0][n][0][1]);
	vector<int> val;
	for(int x=0,i=n,j=0,k=1;x<=20;){
		x++,j+=k,swap(j,k);
		while(i&&k&&dp[x][i][j][k]==dp[x][i-1][j][k-1]+a[i]*x){
			i--,k--;
			val.pb(x);
		}
	}
	reverse(val.begin(),val.end());
	for(int x:val)t[x]++;
	vector<int> id1,id2;
	int pos=n;
	for(int x=20,i=1;~x;x--){
		while(t[x]--)id2.pb(i++);
		vector<int> tmp;
		while(id2.size()&&id1.size()){
			++pos,tmp.pb(pos);
			fa[id2.back()]={pos,1},id2.pop_back(),fa[id1.back()]={pos,2},id1.pop_back();
		}
		id1=id2,id2=tmp;
	}
	for(int i=1;i<=n;i++){
		vector<int> res;
		int u=i;
		while(fa[u].fi){
			res.pb(fa[u].se);
			u=fa[u].fi;
		}
		reverse(res.begin(),res.end());
		for(int w:res)ans[id[i]].pb(w==1?'.':'-');
	}
	for(int i=1;i<=n;i++){
		for(char c:ans[i])putchar(c);puts("");
	}
}