A:真的懵逼了。冷静了半天之后决定二分一下答案,然后先a安排上尽量小的再贪心地一个个扩大。40min才过。这个做法非常怂的以代码复杂度和时间复杂度为代价换取了比较稳的正确性,事实上由于1~n可以组合出1~n*(n+1)/2的所有数,只要从大到小贪心地放就可以了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,a,b,ans,ansa[1000000],ansb[1000000],cnta,cntb;
bool flag[1000000];
bool check(int k)
{
    int w=a;cnta=cntb=0;
    for (int i=1;i<=k;i++)
    if (w>=i) ansa[++cnta]=i,w-=i;
    else break;
    ansa[cnta+1]=k+1;
    for (int i=cnta;i>=1;i--)
    {
        if (!w) break;
        while (w&&ansa[i]+1<ansa[i+1]) ansa[i]++,w--;
    }
    if (b>=1ll*k*(k+1)/2-(a-w)) return 1;
    else return 0;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
#endif
    a=read(),b=read();
    n=sqrt((a+b)*2ll)+2;
    int l=0,r=n;
    while (l<=r)
    {
        int mid=l+r>>1;
        if (check(mid)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    int w=a;cnta=cntb=0;
    for (int i=1;i<=ans;i++)
    if (w>=i) ansa[++cnta]=i,w-=i;
    else break;
    ansa[cnta+1]=ans+1;
    for (int i=cnta;i>=1;i--)
    {
        if (!w) break;
        while (w&&ansa[i]+1<ansa[i+1]) ansa[i]++,w--;
    }
    printf("%d\n",cnta);
    for (int i=1;i<=cnta;i++) printf("%d ",ansa[i]),flag[ansa[i]]=1;
    cout<<endl;
    printf("%d\n",ans-cnta);
    for (int i=1;i<=ans;i++)
    if (!flag[i]) printf("%d ",i);
    return 0;
}
View Code

相关文章: