Hack+求助CF如何Hack
查看原帖
Hack+求助CF如何Hack
557756
Brilliance_Z楼主2023/2/7 17:16

在我 ACAC 这道题后,我对我的做法存疑。于是在机房小伙伴和我的讨论下,成功 HackHack 了我的做法。故发帖,一是提醒谷友们写这道题时不要写这种错误做法,二是询问 CFCF 如何 HackHack

我的做法:

有解的充要条件:点权之和>=(点的个数-1)*x。

所以只要能合并,怎么合并都是对的。

暴力枚举每个点,只要这个点之前没被合并,就遍历它的链表去合并其他点,合并过程中还要把两个链表合并,直到无法继续合并,之后一定会有其他点合并这个无法继续合并的点。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N=3e5+10,M=N*2;
int n,m;
LL x,a[N];
int h[N],t[N],id[M],e[M],ne[M],idx;//t[u]:链表u的末尾;id[idx]:边idx的编号
int p[N],cnt[N];    //p:并查集;cnt[p]:并查集p的总点数
LL tot[N];  //tot[p]:并查集p的点权之和

void add(int u,int v,int i)
{
    e[++idx]=v;
    if(!h[u]) t[u]=idx;
    id[idx]=i;
    ne[idx]=h[u];
    h[u]=idx;
    return ;
}

int find(int x)
{
    return x==p[x] ? p[x] : p[x]=find(p[x]);
}

void merge(int u)
{
    while(find(e[h[u]])==u) h[u]=ne[h[u]];  //把并查集自己到自己的边删除
    for(int i=h[u];i!=0;i=ne[i])
    {
        int v=find(e[i]);
        if(tot[u]+tot[v]>=(cnt[u]+cnt[v]-1)*x)  //满足充要条件,可以合并
        {
            printf("%d\n",id[i]);
            p[v]=u,tot[u]+=tot[v],cnt[u]+=cnt[v];
            
            //把整个链表v插到i的后面
            ne[t[v]]=ne[i];
            ne[i]=h[v];
        }
        while(find(e[ne[i]])==u) ne[i]=ne[ne[i]];   //把并查集自己到自己的边删除
    }
    return ;
}

int main()
{
    scanf("%d%d%lld",&n,&m,&x);
    LL suma=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        suma+=a[i];
        p[i]=i,tot[i]=a[i],cnt[i]=1;
    }
    if(suma<(n-1)*x)
    {
        puts("NO");
        return 0;
    }
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v,i),add(v,u,i);
    }
    puts("YES");
    for(int i=1;i<=n;i++) if(p[i]==i/*之前没被合并*/) merge(i);
    return 0;
}

HackHack 数据:

input:

3 2 10
1 1 100
2 3
1 2

answer:

YES
1
2

wrong output:

YES
1

原因:遍历点1的链表什么都合并不了。遍历点2的链表先遍历点1,无法合并;再遍历点3,可以合并,把点3的链表合并,但是之后依然无法遍历到点1。点3被合并过了,不会遍历点3的链表。

2023/2/7 17:16
加载中...