Luogu3540 [POI2012]SQU-Squarks 题解
首先要对所给序列 sum 升序排序。
设 xi 为所求原序列从小到大排列后第 i 个元素,考虑这样的图:
x1+x2x2+x3x3+x4x4+x5⋯xn−1+xn
x1+x3x2+x4x3+x5⋯xn−2+xn
x1+x4x2+x5⋯xn−2+xn
⋮
x1+xn−1x2+xn
x1+xn
不难发现:x1+x2 与 x1+x3 一定分别是所给序列中第 1、2 小,即 sum1、sum2。既然确定了这两个式子,我们还需要 x2+x3 才能解出 x1、x2、x3 的值。发现 n⩽,所以直接暴力枚举是否满足即可。
当得出 x_1、x_2、x_3 后,其余的和数中 x_3+x_4 最小,由此得出 x_4 的值并能找出 x_2+x_4、x_1+x_4 ,再找其余的和数中最小的 x_4+x_5……以此类推,每次对应一列最底层的和数都会成为最小,推广即可。
View code:
#include<bits/stdc++>
using namespace std;
#define ri register int
#define il inline
const int INF=0x3f3f3f3f,N=310;
int n,m,cnt;
int sum[N*N],p[N*N],ans[N][N];
bool b[N*N];
il ll read(){
ll x=0,y=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
y=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*y;
}
il bool cmp(int x,int y){
return x<y;
}
il void check(int x){
memset(b,0,sizeof(b));
if((sum[1]+sum[2]+sum[x])&1)
return;
p[1]=(sum[1]+sum[2]+sum[x])/2-sum[x];
p[2]=sum[1]-p[1];
p[3]=sum[2]-p[1];
b[1]=b[2]=b[x]=true;
for(ri i=4,j=3;i<=n;i++){
while(j<=m&&b[j])
j++;
if(j>m)
return;
p[i]=sum[j]-p[1];
b[j]=true;
for(ri k=2;k<i;k++){
if (p[k]>p[i])
return;
int v=p[k]+p[i],l=lower_bound(sum+1,sum+m+1,v)-sum,lb=l;
if(sum[l]!=v)
return;
while(lb&&sum[lb]==sum[l])
lb--;
lb++;
while(lb<=m&&sum[lb]==sum[l]&&b[lb])
lb++;
if(sum[lb]!=sum[l]||b[lb]) return;
l=lb;
b[l]=true;
}
}
cnt++;
for(ri i=1;i<=n;i++)
ans[cnt][i]=p[i];
}
signed main(){
n=read();
m=(n*(n-1))>>1;
for(ri i=1;i<=m;i++)
sum[i]=read();
sort(sum+1,sum+m+1,cmp);
for(ri i=3,j=i;i<=m;i=j){
check(i);
while(j<=m&&sum[j]==sum[i])
j++;
}
printf("%d\n",cnt);
for(ri a=1;a<=cnt;a++)
for(ri b=1;b<=n;b++){
printf("%d",ans[a][b]);
if(b==n)
printf("\n");
else
printf(" ");
}
return 0;
}
------------恢复内容开始------------
[题目传送门](https://www.luogu.com.cn/problem/P3540)首先要对所给序列 sum 升序排序。
设 x_i 为所求原序列从小到大排列后第 i 个元素,考虑这样的图:
x_1+x_2\quad x_2+x_3\quad x_3+x_4\quad x_4+x_5\cdots\quad x_{n-1}+x_n
x_1+x_3\quad x_2+x_4\quad x_3+x_5\quad\cdots\quad x_{n-2}+x_n
x_1+x_4\quad x_2+x_5\quad\cdots\quad x_{n-2}+x_n
\vdots
x_1+x_{n-1}\quad x_2+x_n
x_1+x_n
不难发现:x_1+x_2 与 x_1+x_3 一定分别是所给序列中第 1、2 小,即 sum_1、sum_2。既然确定了这两个式子,我们还需要 x_2+x_3 才能解出 x_1、x_2、x_3 的值。发现 n\leqslant300,所以直接暴力枚举是否满足即可。
当得出 x_1、x_2、x_3 后,其余的和数中 x_3+x_4 最小,由此得出 x_4 的值并能找出 x_2+x_4、x_1+x_4 ,再找其余的和数中最小的 x_4+x_5……以此类推,每次对应一列最底层的和数都会成为最小,推广即可。
View code:
#include<bits/stdc++>
using namespace std;
#define ri register int
#define il inline
const int INF=0x3f3f3f3f,N=310;
int n,m,cnt;
int sum[N*N],p[N*N],ans[N][N];
bool b[N*N];
il ll read(){
ll x=0,y=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
y=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*y;
}
il bool cmp(int x,int y){
return x<y;
}
il void check(int x){
memset(b,0,sizeof(b));
if((sum[1]+sum[2]+sum[x])&1)
return;
p[1]=(sum[1]+sum[2]+sum[x])/2-sum[x];
p[2]=sum[1]-p[1];
p[3]=sum[2]-p[1];
b[1]=b[2]=b[x]=true;
for(ri i=4,j=3;i<=n;i++){
while(j<=m&&b[j])
j++;
if(j>m)
return;
p[i]=sum[j]-p[1];
b[j]=true;
for(ri k=2;k<i;k++){
if (p[k]>p[i])
return;
int v=p[k]+p[i],l=lower_bound(sum+1,sum+m+1,v)-sum,lb=l;
if(sum[l]!=v)
return;
while(lb&&sum[lb]==sum[l])
lb--;
lb++;
while(lb<=m&&sum[lb]==sum[l]&&b[lb])
lb++;
if(sum[lb]!=sum[l]||b[lb]) return;
l=lb;
b[l]=true;
}
}
cnt++;
for(ri i=1;i<=n;i++)
ans[cnt][i]=p[i];
}
signed main(){
n=read();
m=(n*(n-1))>>1;
for(ri i=1;i<=m;i++)
sum[i]=read();
sort(sum+1,sum+m+1,cmp);
for(ri i=3,j=i;i<=m;i=j){
check(i);
while(j<=m&&sum[j]==sum[i])
j++;
}
printf("%d\n",cnt);
for(ri a=1;a<=cnt;a++)
for(ri b=1;b<=n;b++){
printf("%d",ans[a][b]);
if(b==n)
printf("\n");
else
printf(" ");
}
return 0;
}
```<p>------------恢复内容结束------------</p>
【推荐】博客园的心动:当一群程序员决定开源共建一个真诚相亲平台
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】Flutter适配HarmonyOS 5知识地图,实战解析+高频避坑指南
【推荐】开源 Linux 服务器运维管理面板 1Panel V2 版本正式发布
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从“看懂世界”到“改造世界”:AI发展的四个阶段你了解了吗?
· 协程本质是函数加状态机——零基础深入浅出 C++20 协程
· 编码之道,道心破碎。
· 记一次 .NET 某发证机系统 崩溃分析
· 微服务架构学习与思考:SOA架构与微服务架构对比分析
· 历时半年,我将一个大型asp.net的零代码快速开发平台转成了java
· C#实现语音预处理:降噪、静音检测、自动增益(附Demo源码)
· 推荐五大AI+MCP自动化测试工具!
· 记一次 .NET 某无语的电商采集系统 CPU爆高分析
· Spring Boot 启动优化实践