YBTOJ 2.4字典树

A.前缀统计

image
image

字典树 顾名思义就是做一个类似于字典的树
根节点往下连边 每条边代表一个字母
对于插入操作 我们从根节点出发往下走
如果有对应的字母边 就继续走到对应的儿子节点
如果没有 就新建一个节点
查询同理 按字母边往下走即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int N = 3e6 + 0721;
int tr[N][70], en[N], cnt;
int n, q, T;

int getnum(char c) {
    if (c >= 'A' && c <= 'Z')
        return c - 'A';
    else if (c >= 'a' && c <= 'z')
        return c + 26 - 'a';
    else
        return c - '0' + 52;
}

void build(string s) {
    int len = s.length();
    int now = 0;
    for (int i = 0; i < len; ++i) {
        int x = getnum(s[i]);
        if (!tr[now][x])
            tr[now][x] = ++cnt;
        //		en[tr[now][x]]++ ;
        now = tr[now][x];
    }
    en[now]++;
}

int query(string s) {
    int sum = 0;
    int len = s.length();
    int now = 0;
    for (int i = 0; i < len; ++i) {
        int x = getnum(s[i]);
        if (!tr[now][x])
            return sum;
        else
            now = tr[now][x], sum += en[now];
    }
    return sum;
}

int main() {
    //	scanf("%d" ,&T ) ;
    //	while(T--){
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; ++i) {
        string s;
        cin >> s;
        build(s);
    }
    for (int i = 1; i <= q; ++i) {
        string s;
        cin >> s;
        printf("%d\n", query(s));
    }

    return 0;
}

B.最大异或对

image
image

看到最大异或 考虑 \(01trie\)
因为 \(100000\) 显然大于 \(011111\) 我们贪心的让高位尽可能是 \(1\) 即可
看到 \(10^5\) 的数据范围 发现枚举其中一个数字 然后查询对应的能达到最大异或的另一个数字即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 0721;
int a[N];
int tr[10000721][2];
int n, cnt, ans;

void build(int x) {
	int now = 0;
	for (int i = 1 << 30; i > 0; i = i >> 1) {
		bool wei = i & x;
		if (!tr[now][wei])
			tr[now][wei] = ++cnt;
		now = tr[now][wei];
//		cout<<i<<" ";
	}
}

int query(int x) {
	int sum = 0, now = 0;
	for (int i = 1 << 30; i > 0; i = i >> 1) {
		bool wei = i & x;
		if (tr[now][!wei]) {
			sum += i;
			now = tr[now][!wei];
		}
		else
			now = tr[now][wei];
//		cout<<"1";
	}
	return sum;
}

int main() {
	
	scanf("%d",&n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d",&a[i]);
		build(a[i]);
	}
	
	for (int i = 1; i <= n; ++i) {
		ans = max(ans , query(a[i]));
	}
	
	printf("%d",ans);
	
	return 0;
}

C.最长异或路径

image
image

还是思考特殊性质 首先这题要做的第一件事就是要整明白怎么统计路径
然后就会非常自然的想到 \(LCA\)
但是要枚举点对的话 复杂度就是 \(O(n^2logn)\)
这不直接T飞!
看看有什么怪异的特殊条件 看到了异或
思考一下异或(特别是异或和)有什么特殊的性质
然后我们发现异或一个数两次跟没异或是一样的
那我们还是考虑长 \(LCA\) 模样的路径统计 发现 \(LCA\) 到根节点的异或和会重复
那就相当于没异或
这样由 \(i\)\(j\) 的异或和就是 \(i\)\(j\) 到根节点的异或和异或起来
然后我们就发现枚举一个端点的时候另一个端点可以直接在 \(01trie\) 上跑
然后就和上一题一样了

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int N = 2000001;
int head[N], nxt[N], v[N], to[N], cnt;
int sum[N];
int tr[N][2], tot;
int ans;
int n;

void cmb(int x, int y, int z) {
    to[++cnt] = y;
    v[cnt] = z;
    nxt[cnt] = head[x];
    head[x] = cnt;
}

void dfs(int x, int fa) {
    for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (y == fa)
            continue;
        sum[y] = sum[x] ^ v[i];
        dfs(y, x);
    }
}

void built(int val, int x) {
    for (int i = 30; i >= 0; i--) {
        int wei = (val >> i) & 1;
        //		cout<<wei ;
        if (tr[x][wei] == 0)
            tr[x][wei] = ++tot;
        x = tr[x][wei];
    }
}

int cx(int val, int x) {
    int re = 0;
    for (int i = 30; i >= 0; i--) {
        int wei = (val >> i) & 1;
        //		cout<<wei ;
        if (tr[x][!wei] != 0) {
            re += (1 << i);
            x = tr[x][!wei];
        } else
            x = tr[x][wei];
    }
    return re;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i < n; ++i) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        cmb(x, y, z);
        cmb(y, x, z);
    }

    dfs(1, -1);

    for (int i = 1; i <= n; ++i) built(sum[i], 0);

    for (int i = 1; i <= n; ++i) ans = max(ans, cx(sum[i], 0));

    printf("%d", ans);

    return 0;
}

D.阅读理解

image
image

对于这题 我只能说 \(STL\) 大法好

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N = 1e3 + 0721;
bool vis[N];

map<string,vector<int> > m;

int main() {
	
	int n;
	scanf("%d",&n);
	for (int i = 1; i <= n; ++i) {
		int num;
		scanf("%d",&num);
		for (int j = 1; j <= num; ++j) {
			string s;
			cin >> s;
			m[s].push_back(i);
		}
	}
	
	int q;
	scanf("%d",&q);
	for (int i = 1; i <= q; ++i) {
		memset(vis, 0, sizeof(vis));
		string s;
		cin >> s;
		for (int j = 0; j < m[s].size(); ++j) {
			if (!vis[m[s][j]]) {
				printf("%d ",m[s][j]);
				vis[m[s][j]] = 1;
			}
		}
		printf("\n");
	}
	
	return 0;
}
posted @ 2023-06-28 17:38  Steven24  阅读(54)  评论(0编辑  收藏  举报