BZOJ3421: Poi2013 Walk

探讨单位超立方体图中,在移除部分节点后如何判断两点是否仍保持连通。采用反证法证明了剩余节点大于特定数量时,至多存在一个大连通块,并给出基于此理论的BFS搜索算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:有 2n 个长度为n的01串,两个01串之间有边当且仅当这两个01串只有一位不同,现在从这 2n 个串中拿掉k个,问指定两个串之间能否到达

吐槽:
先给这题100个差评,这题无论是POI官网还是BZOJ都特别差
先说POI官网,给的题解直接来了个定理也没证,然后说了一句这个定理在opisu中已经给出了,我拿百度翻译翻译了一下发现opisu是description 的意思…可是description 里没给啊,然后我就翻了整个POI2013的波兰文题解也没找到在哪,那就只能把这个定理当成已知了
再说BZOJ,这题总时限开50s,官网上单点都有120s好吗!各种卡常数,最后照着Claris的代码一点一点改,结果把循环变量i开成全局变量就A了…..卡了一上午测评差评!

题解:
先说那个POI官方给的题解里的定理吧,对于一个单位超立方体,如果我们把所有顶点划分成两个集合,这两个点集之间的边的数量至少是两个点集中较小的那一个的个数,也就是
|(u,v):uS,vVS|­­min(|S|,|VS|).
这个我也不会证,不过既然官方说这个题目里告知了,我们就把他当成定理吧

接着我们来证明对于一个单位超立方体,假如挖掉k个节点,剩下的结点个数大于nk的连通块至多有一个
用反证法
假设有两个这样的连通块,另 S 为其中一个连通块的点集,Vk为另一个的点集,则有 VkVS |VS||Vk|nk+1
则由定理可知S与V-S这两个集合之间的边至少为nk+1条
接着因为一共删除了k个点,而每个点都向外连了n条边,所以最多最多只能删掉这nk+1条中的nk条边,也就是说S和V-S一定还有边相连,这就与之前假设的S是一个独立的连通块矛盾了
所以剩下的结点个数大于nk的连通块至多有一个

所以我们只需要从一个节点开始BFS,限制最多扩展nk步,如果两个都扩展了nk步还没搜完,那就说明他俩在同一个大块里
各种卡常数啊,unordered_map貌似都不行,只能手写hash链表

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5001000
#define M 9999991
using namespace std;
int i,head[M],nxt[N],cnt;
long long v[N];
inline void add(long long x)
{
    int T=x%M,i;
    cnt++;nxt[cnt]=head[T];
    head[T]=cnt;v[cnt]=x;
}
long long q[N],h,t;
inline void checkandadd(long long x)
{
    int T=x%M,i;
    for(int i=head[T];i;i=nxt[i])
    if(v[i]==x) return;
    cnt++;nxt[cnt]=head[T];
    head[T]=cnt;v[cnt]=x;
    t++;q[t]=x;
}
int n,k;
char s[101];
inline long long dec()
{
    scanf("%s",s);
    long long ret=0,i;
    for(int i=0;i<n;i++)
    ret=ret*2+s[i]-48;
    return ret;
}
long long S,E;
long long a[N];
int main()
{
    scanf("%d%d",&n,&k);
    int nk=n*k+5;
    long long x,y;
    S=dec();
    E=dec();
    for(i=1;i<=k;i++)
    {
        a[i]=dec();
        add(a[i]);
    }
    h=t=1;q[1]=S;
    add(S);
    while(h<=t&&t<=nk)
    {
        x=q[h];h++;
        if(x==E) {puts("TAK");return 0;}
        for(i=0;i<n;i++)
        checkandadd(x^(1LL<<i));
    }
    if(t<=nk) {puts("NIE");return 0;}
    h=t=1;q[1]=E;
    cnt=0;
    for(i=0;i<M;i++)
    head[i]=0;
    add(E);
    for(i=1;i<=k;i++)
    add(a[i]);
    while(h<=t&&t<=nk)
    {
        x=q[h];h++;
        if(x==S) {puts("TAK");return 0;}
        for(i=0;i<n;i++)
        checkandadd(x^(1LL<<i));
    }
    if(t<=nk) puts("NIE");
    else puts("TAK");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值