首页 > 其他分享 >LG-P3552 [POI2013]SPA-Walk 题解

LG-P3552 [POI2013]SPA-Walk 题解

时间:2022-10-13 08:44:07浏览次数:77  
标签:LG 连通 vert int 题解 ll times Walk left

LG-P3552 [POI2013]SPA-Walk Solution

目录

更好的阅读体验戳此进入

(建议您从上方链接进入我的个人网站查看此 Blog,在 Luogu 中图片会被墙掉,部分 Markdown 也会失效)

题面

存在 $ 2^n $ 个长度为 $ n $ 的 $ 01 $ 串,两个 $ 01 $ 串之间有边当且仅当两者之间仅有一位不同,或者说 $ x, y $ 之间有边当且仅当 $ x \oplus y = 2^t $。我们称这样的一个图为 $ n $ 维超立方体(n-dimensional hipercube)。现在从这其中去掉 $ k $ 个串 $ s_1, s_2, \cdots, s_k $,给定两个串 $ S, T $,求两者之间是否能够到达。

能到达输出 TAK,反之输出 NIE

$ 1 \le n \le 60, 0 \le k \le \min(10^6, 2^n - 1), n \times k \le 5 \times 10^6 $。

输入格式

n k

S T

$ s_1 $

$ s_2 $

$ \cdots $

$ s_k $

(偷个懒就不写文字叙述了。。。)

Solution

这道题我本来还以为是和 LG-P7966 [COCI2021-2022#2] Hiperkocka 差不多的题,该题题解,按照那道题的思路想了一下然后假了,改了一下之后 T 掉了,寄,和之前的题关系不是很大,只是定义一样。

Lemma 1:对于一个 $ n $ 维超立方体,令点集为 $ S $,将其所有的点分为两个点集 $ S_1, S_2 \(,\) S_1 \cup S_2 = S, S_1 \cap S2 = \phi $,则两点集之间的边集的大小一定不小于两点集之间较小者的大小,也就是 $ \left\vert { (u, v) \vert u \in S_1, v \in S_2, u \oplus v = 2^t } \right\vert = \min(\left\vert S_1 \right\vert, \left\vert S_2 \right\vert) $。

证明:A proof is left to the reader.

证明:这东西大多数地方都没有人证明,甚至官方题解,感觉可以当成一个已知结论记住,关于严谨证明网上也找到了个大佬的证明(看不懂),戳此进入

Lemma 2:对于一个 $ n $ 维超立方体,将其删去 $ k $ 个点后,最多仅能够形成 $ 1 $ 个点集大小大于 $ n \times k $ 的连通块。

证明:假设我们现在有两个连通块,其点集分别为 $ S_1, S_2 $,显然 $ S_1 \cap S_2 = \phi $,我们设 $ \left\vert S_1 \right\vert, \left\vert S_2 \right\vert \ge n \times k $,令点的全集为 $ S $,则显然 $ S_2 \subset S \cap \overline{S_1} $。我们又可以通过 Lemma 1 知道 $ S_1 $ 和 $ S_2 \subset S \cap \overline{S_1} $ 之间的边集大小不小于 $ \min(\left\vert S_1 \right\vert, \left\vert S \cap \overline{S_1} \right\vert) $,也就是其边集 $ E $ 满足 $ E \ge n \times k + 1 $,也就是说如果我们想要形成这两个连通块就必须要断开至少 $ n \times k + 1 $ 条边,而我们去掉 $ k $ 个点只能断开 $ n \times k $ 条边,无法形成这两个连通块,$ \square $。

于是只要有了 Lemma 2,我们就可以很容易想到,令出发点和终点分别为 $ S, T $,如果两者可以互通,那么他们要么都在唯一的大于 $ n \times k $ 的连通块内,要么在某一个小于等于 $ n \times k $ 的连通块内,所以我们只需要搜至多 $ n \times k $ 的点数,如果两者都不在小于 $ n \times k $ 的连通块中,那么一定同在唯一的大连通块中,反之如果在同一个连通块内那么一定可以被搜到。

于是我们分别从 $ S, T $ 进行爆搜,各自维护一个 unordered_set 记录其所在连通块有哪些点,如果从搜索过程中遇到另一个点了,则两者同在一个小连通块中,直接输出连通然后 exit,如果连通块大小超过 $ n \times k $ 了那么直接标记一下然后 return。两者都搜完后如果两个连通块都超过 $ n \times k $ 了那么一定同在一个大块,反之则一定不在同一个块,不连通。

另外个人建议把二进制转成 long long 再存,否则还会存在一个 hash 的复杂度,虽然我感觉好像嗯搞也能过???

然后写位移的时候记得写成 1ll

这个的复杂度大概是 $ O(n^2k) $ 级别的,但是常数可能会很大,然后我不太确定这玩意能不能被卡成类似 $ O(2^n) $ 级别的,但是因为有 $ n \times k $ 这个限制感觉似乎是可以被严谨证明的,我不会

A proof is left to the reader.

然后。。。Luogu 评测记录

unordered_set 寄了,cc_hash_table 寄的更多,经典被卡常,调了亿会怎么 Luogu 上都是两个点 TLE,一个点 MLE,于是开始手搓 hash 挂链,队列之类的,最终终于在 Luogu 上把这破题给卡过去了。。。

Code

#define _USE_MATH_DEFINES
#include <bits/extc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

/******************************
abbr
uex => unexist
******************************/

using namespace std;
using namespace __gnu_pbds;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template<typename T = int>
inline T read(void);

ll str2ll(char* s, int n){
    ll ret(0), base(0);
    for(int i = n - 1; i >= 0; --i)
        ret += (1ll << (base++)) * (s[i] == '0' ? 0 : 1);
    return ret;
}
#define MOD 2737321
struct Edge{
    Edge* nxt;
    ll val;
    OPNEW;
}eData[4145149];
ROPNEW(eData);
Edge* head[2737330];
void clear(void){memset(head, 0, sizeof(head));}
void Add(ll val){
    int idx = val % MOD;
    head[idx] = new Edge{head[idx], val};
}
bool Check(ll val){
    int idx = val % MOD;
    for(auto i = head[idx]; i; i = i->nxt)
        if(i->val == val)return true;
    return false;
}

ll S, T;
int N, K;
ll cur[5100000];
int fnt(0), til(0);
void bfs(ll S, ll T){
    fnt = til = 0;
    cur[til++] = S;
    Add(S);
    while(fnt < til){
        ll tp = cur[fnt++];
        for(int base = 0; base <= N - 1; ++base){
            ll val = tp ^ (1ll << base);
            if(val == T)printf("TAK\n"), exit(0);
            if(Check(val))continue;
            else{
                Add(val);
                cur[til++] = val;
            }
        }
        if(til > N * K)return;
    }
    printf("NIE\n"); exit(0);
}
char c[65];
vector < ll > values;
int main(){
    // freopen("in.txt", "r", stdin);
    N = read(), K = read();
    scanf("%s", c); S = str2ll(c, N);
    scanf("%s", c); T = str2ll(c, N);
    if(S == T)printf("TAK\n"), exit(0);
    for(int i = 1; i <= K; ++i){
        scanf("%s", c); ll tmpp = str2ll(c, N);
        values.push_back(tmpp);
    }
    for(auto i : values)Add(i);
    bfs(S, T);
    clear();
    for(auto i : values)Add(i);
    bfs(T, S);
    printf("TAK\n");
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template<typename T>
inline T read(void){
    T ret(0);
    short flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

UPD

update-2022_09_27 初稿

标签:LG,连通,vert,int,题解,ll,times,Walk,left
From: https://www.cnblogs.com/tsawke/p/16786788.html

相关文章