题意:有n个互不相同的整数: p1,p2,…,pn 。把这些整数分到两个集合A和B里边。但是要符合下面两个条件。

如果x属于A,那么a-x也肯定属于A。

如果x属于B,那么b-x也肯定属于B。

判断一下是否存在一种方案来分配这些数字到集合A,B中。

注意:如果一个集合为空也是可以的。

思路:思路是很简单的就是迷之WA…牛客上面的数据水得可以快排+二分都过了下标明明都不对…果然是用脚造的数据…然后用并查集做结果一直WA在第8组数据…可是找不出来哪儿错了…找题解结果都是千篇一律的代码 可怕难道你们的思路都一模一样的嘛qwq本弱找不出错哪儿了先放在这儿…

update:查出错误了 a-num[i]可以不等于b-num[i]啊 少考虑了一种a-num[i]存在而且b-num[i]存在的情况

对于一个x来说,能分成以下4种情况(这里a!=b):

1.a-x不存在,b-x不存在。这种情况直接输出NO。

2.a-x存在,b-x不存在。这种情况把x和a-x放在集合A中。

3.a-x不存在,b-x存在。这种情况把x和b-x放在集合B中。

4.a-x存在,b-x存在。这种情况比较我们就不能直接确定要放A还是要放B了。

1⃣把x和a-x放入集合A,那么b-x肯定也要放入集合A,那么就说明存在y使得y=a-b+x。

2⃣把x和b-x放入集合B,那么a-x肯定也要放入集合B,那么就说明存在y使得y=b-a+x。

假设有y=a-b+x和y=b-a+x同时存在,则有a=b,矛盾。所以上面两种情况只可能存在一种或都不存在。如果都不存在,那么我们就无法决定把x和a-x和b-x放到集合A中还是集合B中,因为放到那一边都会剩下另一个无法去放,所以就输出NO。这里用并查集来维护即可。

代码

WA代码:

int n;
int par[100010],high[100010],num[100010];
map<int,int>mp;
void init()
{
    for(int i=0;i<=n+1;i++)
    {
        par[i]=i;
        high[i]=0;
    }
}
int find(int x)
{
    if(par[x]==x)return x;
    return par[x]=find(par[x]);
}
void unite(int x,int y)
{
    x=find(x);
    y=find(y);
    if(x==y)return;
    if(high[x]<high[y])par[x]=y;
    else
    {
        par[y]=x;
        if(high[x]==high[y])high[x]++;
    }
}
bool same(int x,int y)
{
    return find(x)==find(y);
}
int main()
{
    int a,b,MAX=-1;
    scanf("%d%d%d",&n,&a,&b);
    for(int i=2;i<=n+1;i++)
    {
        scanf("%d",&num[i]);
        MAX=max(MAX,num[i]);
        mp[num[i]]=i;
    }
    if(MAX>=max(a,b)){printf("NO\n");return 0;}
    init();
    int flag=0;
    for(int i=2;i<=n+1&&flag==0;i++)
    {
        //printf("i=%d par[i]=%d find(i)=%d\n",i,par[i],find(i));            //printf("i=%d\n",i);
        int sign=0;
        if(mp[b-num[i]])
        {
            //printf("(%d)\n",par[1]);
            //printf("!\n");
            unite(1,i);
            unite(1,mp[b-num[i]]);
            sign++;
        }
        if(sign==0&&mp[a-num[i]])
        {
            //printf("?\n");
            unite(0,i);
            unite(0,mp[a-num[i]]);
            sign++;
        }
        if(sign==0)flag++;
    }
    if(flag!=0)printf("NO\n");
    else
    {
        printf("YES\n");
        for(int i=2;i<n+1;i++)
            printf("%d ",find(i));
        printf("%d\n",find(n+1));
    }
    return 0;
}

AC代码:

int n;
int par[100010],high[100010],num[100010];
map<int,int>mp;
void init()
{
    for(int i=0;i<=n+1;i++)
    {
        par[i]=i;
        high[i]=0;
    }
}
int find(int x)
{
    if(par[x]==x)return x;
    return par[x]=find(par[x]);
}
void unite(int x,int y)
{
    x=find(x);
    y=find(y);
    if(x==y)return;
    if(high[x]<high[y])par[x]=y;
    else
    {
        par[y]=x;
        if(high[x]==high[y])high[x]++;
    }
}
bool same(int x,int y)
{
    return find(x)==find(y);
}
int main()
{
    int a,b,MAX=-1;
    scanf("%d%d%d",&n,&a,&b);
    for(int i=2;i<=n+1;i++)
    {
        scanf("%d",&num[i]);
        MAX=max(MAX,num[i]);
        mp[num[i]]=i;
    }
    if(MAX>=max(a,b)){printf("NO\n");return 0;}
    init();
    for(int i=2;i<=n+1;i++)
    {
        if(mp[b-num[i]])unite(i,mp[b-num[i]]);
        else unite(i,0);
        if(mp[a-num[i]])unite(i,mp[a-num[i]]);
        else unite(i,1);//因为只能属于其中一类
    }
    if(find(0)==find(1))printf("NO\n");
    else
    {
        printf("YES\n");
        for(int i=2;i<=n+1;i++)
        {
            if(find(i)==find(0))printf("0");
            else printf("1");
            if(i!=n+1)printf(" ");
        }
        printf("\n");
    }
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

51nod1019 树状数组求逆序数 Previous
Wannafly挑战赛10 Next