BZOJ 2797 [Poi2012]Squarks 数学

题意:
Po姐姐生了n个孩子,现在给出这n个孩子任意两个孩子年龄之和组成的序列,求出每一个孩子的年龄,如果有多种方案,那么按照字典序从大到小输出。
解析:
其实挺容易的数学题,然而并没有想出来。
我们不妨使n个孩子的年龄呈升序。
首先我们可以肯定的是,这n*(n-1)>>1个数里,最小的一定是第一个孩子与第二个孩子的年龄之和,第二小的一定是第一个和第三个孩子的年龄之和。
并且我们知道,比第二个与第三个孩子年龄之和大的只有可能是1+4,1+5,1+6…..所以最多不超过n-3个。
并且如果我们知道了第1,2,3个孩子的年龄的话,那么所有孩子的年龄就都知道了。
因为如果我们知道了第1,2,3个孩子的年龄的话,在集合里删除这三个孩子的年龄组合后,最小的一定是1,4的组合,于是我们就知道了第四个,再在集合中删去第四个孩子与第1,2,3个孩子的年龄组合后,最小的一定是1,5的组合,以此类推。
所以现在我们的关键就是求出2,3孩子的年龄和。
又之前谈到过,除了1,2组合与1,3组合外比2,3孩子年龄和小的不超过n-3个,所以我们考虑暴力枚举,2,3孩子的年龄和。
然后我们需要一个可以支持添加元素,删除元素,查询最小值操作的元素可重复数据结构,multiset即可实现。
复杂度:O(n*n*(n-1)/2*logn))
代码:

#include <set>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 310
using namespace std;
int n;
int a[N*N];
multiset<int>s;
int top;
int ans[N][N];
int topp;
int sta[N];
void check(int sum3)
{
    s.clear();
    for(int i=3;i<=n*(n-1)/2;i++)
        s.insert(a[i]);
    s.erase(s.find(sum3));
    if((a[1]+a[2]+sum3)&1)return;
    int tmp=(a[1]+a[2]+sum3)>>1;
    sta[1]=tmp-sum3,sta[2]=tmp-a[2],sta[3]=tmp-a[1];
    if(sta[1]<0||sta[2]<0||sta[3]<0)return;
    for(int i=4;i<=n;i++)
    {
        int val=*s.begin();
        sta[i]=val-sta[1];
        if(sta[i]<0)return;
        for(int j=1;j<i;j++)
        {
            int tmp=sta[i]+sta[j];
            if(s.find(tmp)==s.end())return;
            s.erase(s.find(tmp));
        }
    }
    ans[0][0]++;
    for(int i=1;i<=n;i++)
        ans[ans[0][0]][i]=sta[i];
}
int main()
{
//  freopen("B.in","r",stdin);
//  freopen("B.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n*(n-1)/2;i++)
        scanf("%d",&a[i]);
    sort(a+1,a+n*(n-1)/2+1);
    for(int i=3;i<=n;i++)
        if(i==3||a[i]!=a[i-1])
        check(a[i]);
    printf("%d\n",ans[0][0]);
    for(int i=1;i<=ans[0][0];i++)
    {
        for(int j=1;j<=n;j++)
            printf("%d ",ans[i][j]);
        puts("");
    }
//  fclose(stdin);fclose(stdin);return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值