2023.7.15 记录

这几天了解了点之前没学过的,但还没有深入了解,所以先放个板子。

P8306 【模板】字典树

typedef struct node {
    int flag;
    struct node* key[65] = { nullptr };
}node;
node* root;

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

void biu(string s) {
    node* rt = root;
    for (int i = 0; i < s.size(); i++) {
        int v = getid(s[i]);
        if (!(rt->key[v])) {
            node* p = new node;
            p->flag = 0;
            rt->key[v] = p;
        }
        rt = rt->key[v];
        rt->flag++;
    }
}

int query(string s) {
    int ans = 0;
    node* rt = root;
    for (int i = 0; i < s.size(); i++) {
        int v = getid(s[i]);
        if (!(rt->key[v])) {
            return 0;
        }
        rt = rt->key[v];
    }
    ans += rt->flag;
    return ans;
}

void solve() {
    int n, m;
    cin >> n >> m;
    string s;
    root = new node;
    rep(i, 1, n) {
        cin >> s;
        biu(s);
    }
    rep(i, 1, m) {
        cin >> s;
        cout << query(s) << endl;
    }
    delete root;
}

P3805 【模板】manacher 算法

string Manastr(string s) {
    int n = s.length();
    if (n == 0) {
        return "^$";
    }
    string ret = "^";
    for (int i = 0; i < n; i++) {
        ret += '#', ret += s[i];
    }
    ret += "#$";
    return ret;
}

string Manacher(string s) { 
    string T = Manastr(s);
    int n = T.length();
    int* P = new int[n];
    int M = 0, R = 0;
    for (int i = 1; i < n - 1; i++) {
        int i_mirror = 2 * M - i;
        if (R > i) {
            P[i] = min(R - i, P[i_mirror]);// 防止超出 R
        }
        else {
            P[i] = 0;// 等于 R 的情况
        }
        // 碰到之前讲的三种情况时候,需要利用中心扩展法
        while (T[i + 1 + P[i]] == T[i - 1 - P[i]]) {
            P[i]++;
        }
        // 判断是否需要更新 R
        if (i + P[i] > R) {
            M = i;
            R = i + P[i];
        }
    }
    // 找出 P 的最大值
    int maxLen = 0;
    int centerIndex = 0;
    for (int i = 1; i < n - 1; i++) {
        if (P[i] > maxLen) {
            maxLen = P[i];
            centerIndex = i;
        }
    }
    int start = (centerIndex - maxLen) / 2; //求原字符串下标
    return s.substr(start, maxLen);
}

void solve() {
    string s;
    cin >> s;
    s = Manacher(s);
   // cout << s << '\n';
    cout << s.size() << '\n';
}

P1439 【模板】最长公共子序列

求a,b两个数组中的最长公共子序列。
数组都是1-n的数字,用mp数字标记b中的值在a中出现的位置,a,b两个数组的最长公共子序列在mp中是递增的。
这样一来问题便转化成了求mp数组中的最长上升序列,时间复杂度O(n*logn)。

int a[100005];
int b[100005];
int mp[100005];
int f[100005];

void solve() {
	int n;
	cin >> n;
	rep(i, 1, n) {
		cin >> a[i];
		mp[a[i]] = i;
		f[i] = 1e9;
	}
	rep(i, 1, n) cin >> b[i]; 
	int len = 1;      //求最长上升序列
	f[1] = mp[b[1]];    
	rep(i, 2, n) {
		int j = mp[b[i]];
		if(j > f[len]) {    //接在后面
			f[++len] = j;
			continue;
		}
		int l = 1, r = len;
		while (l < r) {       //二分求f中第一个大于等于j的
			int mid = (l + r) >> 1;
			if(f[mid] >= j)r = mid;
			else l = mid + 1;
		}
		f[l] = j;   //替代
	}
	cout << len << endl;
}

P1020 [NOIP1999 普及组] 导弹拦截

两问:求最多能拦截多少导弹,,,最少要配备多少套这种导弹拦截系统
因为导弹高度要越来越低,所以我们很容易发现第一问就是求最长下降子序列
第二问也就是求最少的不上升子序列的个数
Dilworth 定理: 最少的不上升子序列的个数就是最长上升子序列的长度
所以这题:最长下降子序列+最长上升子序列。

这题没什么难度,但是包括了最长下降子序列+最长上升子序列。

int a[100005];
int f[100005];
void solve() {
	int n = 0;
	while(cin >> a[++n]);
	n--;
	f[0] = 1e9;
	int len = 1;
	f[1] = a[1];
	rep(i, 2, n) {     //最长下降子序列
		if(a[i] <= f[len]) {
			f[++len] = a[i];
		}
		else {
			int l = 1, r = len;
			while(l < r) {
				int mid = (l + r) / 2;
				if(f[mid] < a[i]) r = mid;
				else l = mid + 1;
			}
			f[l] = a[i];
		}
	}
	cout << len << endl;
	rep(i, 1, len) f[i] = 0;
	len = 1;
	f[1] = a[1];
	rep(i, 2, n) {    //最长上升子序列
		int l = 1, r = len;
		if(a[i] > f[len]) {
			f[++len] = a[i];
			continue;
		}
		while(l < r) {
			int mid = (l + r) / 2;
			if(f[mid] >= a[i]) r = mid;
			else l = mid + 1;
		}
		f[l] = a[i];
	}            
	cout << len <<endl;
}

P3379 【模板】最近公共祖先(LCA)

两种解法

倍增法求LCA
步骤:
预处理:dfs,获取每一个结点的深度及祖先
求lca:先把深度较深的那个结点跳到与另一个结点同样的深度,然后两个结点一起跳直到相等。

const int MAXN = 1e6 + 10;
int dep[MAXN];
vector<int>g[MAXN];
int f[MAXN][21];

void dfs(int u, int fa) {   //预处理
    dep[u] = dep[fa] + 1;
    f[u][0] = fa;
    for (int i = 1; (1 << i) <= dep[u]; i++)
        f[u][i] = f[f[u][i - 1]][i - 1];
    for (int v : g[u]) {
        if (v != fa) {
            dfs(v, u);
        }
    }
}

int lca(int u, int v) {  //求lca
    if (dep[u] < dep[v])
        swap(u, v);
    for (int i = 20; i >= 0; i--)
        if (dep[f[u][i]] >= dep[v])
            u = f[u][i];
    if (u == v)
        return v;
    for (int i = 20; i >= 0; i--) {
        if (f[u][i] != f[v][i]) {
            u = f[u][i];
            v = f[v][i];
        }
    }
    return f[u][0];
}

void solve() {
    int n, m, s;
    cin >> n >> m >> s;
    rep(i, 1, n - 1) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(s, 0);
    rep(i, 1, m) {
        int u, v;
        cin >> u >> v;
        cout << lca(u, v) << endl;
    }
}

Tarjan(离线)算法   O(n+q)
步骤:
1任选一个点为根节点,从根节点开始。
2遍历该点u所有子节点v,并标记这些子节点v已被访问过。
3若是v还有子节点,返回2,否则下一步。
4合并v到u上。
5寻找与当前点u有询问关系的点v。
6若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。
遍历的话需要用到dfs来遍历,利用并查集来合并两个节点。

const int MAXN = 1e6 + 10;
int n;

vector<int>g[MAXN];
vector<PII>query[MAXN];
int pre[MAXN];
int lca[MAXN];
bool vis[MAXN];

int find(int f) {
	if (pre[f] == f) return f;
	else return pre[f] = find(pre[f]);
}

void marge(int f1, int f2) {
	f1 = find(f1);
	f2 = find(f2);
	pre[f2] = f1;
}

void init() {
	rep(i, 1, n)pre[i] = i, vis[i] = false;
}

void tarjan(int u) {
	vis[u] = true;
	for(int v : g[u]) {
		if(vis[v]) continue;
		tarjan(v);
		marge(u, v);
	}
	for(auto t : query[u]) {
		int to = t.first, id = t.second;
		if(vis[to]) {
			lca[id] = find(to);
		}
	}
}

void solve() {
	int m, rt;
	cin >> n >> m >> rt;
	rep(i, 1, n - 1) {
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	rep(i, 1, m) {
		int u, v;
		cin >> u >> v;
		query[u].push_back({v, i});
		query[v].push_back({u, i});
	}
	init();
	tarjan(rt);
	rep(i, 1, m) {
		cout << lca[i] <<endl;
	}
}

P1462 通往奥格瑞玛的道路

二分+最短路

牛客上的松鼠回家加强版

const int N = 1e5 + 10;
struct node {
	int to, w, nxt;
}edge[N];
int head[N];
int qom[N];
int n, m, k;
int cnt;
void add(int u, int v, int w) {
	edge[++cnt].to = v;
	edge[cnt].w = w;
	edge[cnt].nxt = head[u];
	head[u] = cnt;
}

ll dis[N];
bool vis[N];

void dijkstra(int x) {
	rep(i, 1, n)dis[i] = 1e18, vis[i] = false;
	dis[1] = 0;
	priority_queue<PII, vector<PII >, greater<PII > >q;
	q.push({0, 1});
	while(!q.empty()) {
		int u = q.top().second;
		q.pop();
		if(vis[u])continue;
		vis[u] = true;
		for (int e = head[u]; e; e = edge[e].nxt) {
			int v = edge[e].to, w = edge[e].w;
			if(qom[v] <= x && dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				if(!vis[v])
					q.push({dis[v], v});
			} 
		}
	}
}

bool check(int x) {
	dijkstra(x);
	if(dis[n] <= k)return true;
	return false;
}

void ef() {
	int l = max(qom[1], qom[n]), r = 1e9 + 1;
	while(l < r) {
		int mid = (l + r) >> 1;
		if(check(mid)) {
			r = mid;
		}
		else l = mid + 1;
	}
	if(l == 1e9 + 1)cout << "AFK" <<endl;
	else cout << l << endl;
}

void solve() {
	cin >> n >> m >> k;
	rep(i, 1, n)cin >> qom[i];
	while(m--) {
		int u, v, w;
		cin >> u >> v >> w;
		add(u, v, w);
		add(v, u, w);
	}
	ef();
}

P1168 中位数 

树状数组+离散化+二分

相当于复习了一下前面写的那个中位数。

int a[100006];
int t[100006];
int b[100006];
int m;int n;
int lowbit(int x) {
	return x & (-x);
}

void add(int p, int k) {
	for(int i = p; i <= 1e5 + 5; i += lowbit(i)) {
		t[i] += k;
	}
}

int getsum(int p) {
	int ret = 0;
	for(int i = p; i; i -= lowbit(i)) {
		ret += t[i];
	}
	return ret;
}

int ef() {
	int l = 1, r = 1e5;
	int k = (m + 1) / 2;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(getsum(mid) >= k) {
			r = mid - 1;
		}
		else l = mid + 1;
	}
	return a[l];
}

int lower(int x) {
	int l = 1, r = n;
	int mid;
	while (l <= r) {
		mid = (l + r) >> 1;
		if(a[mid] > x) {
			r = mid - 1;
		}
		else if(a[mid] < x) {
			l = mid + 1;
		}
		else return mid;
	}
	return -1;
}

void solve() {
	
	cin >> n;
	rep(i, 1, n) {
		cin >> a[i];
		b[i] = a[i];
	}
	sort(a + 1, a + 1 + n);
	
	rep(i, 1, n) {
		b[i] = lower(b[i]);
	}
	
	rep(i, 1, n) {
		m++;
		add(b[i], 1);
		if(i & 1) {
			cout << ef() << endl;
		}
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akb000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值