题解 P3540 【[POI2012]SQU-Squarks】
题意(题面上的有毒)
给出一个
题解
定义
假设
先给
已知x_1 求所有的x
先假设我们已经知道
可以轻易看出
因为
如果上面的式子成立,我们就可以用这样的方式在知道
但是很明显,上面我们推导出来的性质并不适用于当
那么想想用什么方法让上面的性质成立
因为只有
如何求x_1
我们已知
如果我们能够知道
因为
代码
记得考虑
时间复杂度不高,用的map映射
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
const int M=301;
int n,m,ans;
int x[M][M];
int v[N],tot[N],cost[N];
map<int,int>mp;//直接开数组空间开不下,map映射值为v的数的数量
void init_v()//预先处理好v
{
scanf("%d",&n);
m=n*(n-1)/2;
for(int i=1;i<=m;i++)
scanf("%d",&v[i]);
sort(v+1,v+m+1);//排序
for(int i=1;i<=m;i++)//映射v[i]的值的位置,便于以后统计剩余数量
{
if(!mp[v[i]]) mp[v[i]]=i;//v[i]没有出现过,给予新位置(也可以开个cnt,然后mp[v[i]]=++cnt,我比较懒)
tot[mp[v[i]]]++;
}
}
bool check(int a,int b,int c)//判断是否需要继续计算
{
if(a<=0||b<=0||c<=0) return 0;
if(a>=b||a>=c||b>=c) return 0;
if(x[ans][1]==a&&x[ans][2]==b&&x[ans][3]==c) return 0;//防止重复的数导致重复同样的答案,ans还没加,这是上一次算出的答案
ans++;
x[ans][1]=a;x[ans][2]=b;x[ans][3]=c;//符合条件,更新x[1],x[2],x[3]
return 1;
}
bool getx()//判断x1,x2,x3是否符合条件,并且求出剩下的x
{
bool flag=0;
memset(cost,0,sizeof(cost));
//去掉x1,x2,x3组成的数
cost[mp[x[ans][1]+x[ans][2]]]++;
cost[mp[x[ans][1]+x[ans][3]]]++;
cost[mp[x[ans][2]+x[ans][3]]]++;
int now=3;
for(int i=4;i<=n&&!flag;i++)//从4开始,小于4的已经处理好了
{
while(tot[mp[v[now]]]-cost[mp[v[now]]]==0) now++;//找到还没被使用的最小的数
x[ans][i]=v[now]-x[ans][1];
for(int j=1;j<i;j++)//去掉x[1]~~x[i-1]与x[i]组成的数
{
int pos=mp[x[ans][i]+x[ans][j]];
cost[pos]++;
if(cost[pos]>tot[pos]) flag=1;//不符合条件
}
}
return flag;
}
void solve()
{
int t1=v[1];//x1+x2;
int t2=v[2];//x1+x3;
for(int i=3,a,b,c;i<=n;i++)//i从3枚举,不会遍历到t1和t2
{
b=(v[i]-t2+t1)/2;//小学的解方程
c=v[i]-b;a=t1-b;
if(check(a,b,c)) ans-=getx();
}
//输出答案
printf("%d\n",ans);
for(int i=1;i<=ans;i++)
{
for(int j=1;j<=n;j++)
printf("%d ",x[i][j]);
puts("");
}
}
int main()
{
init_v();
solve();
return 0;
}
这题不看题解的话还是有点难度的