这几天了解了点之前没学过的,但还没有深入了解,所以先放个板子。
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;
}
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';
}
求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;
}
两问:求最多能拦截多少导弹,,,最少要配备多少套这种导弹拦截系统
因为导弹高度要越来越低,所以我们很容易发现第一问就是求最长下降子序列
第二问也就是求最少的不上升子序列的个数
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;
}
两种解法
倍增法求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;
}
}
二分+最短路
牛客上的松鼠回家加强版
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();
}
树状数组+离散化+二分
相当于复习了一下前面写的那个中位数。
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;
}
}
}