首页 > 其他分享 >HDU-ACM 2024 Day4

HDU-ACM 2024 Day4

时间:2024-08-17 20:04:28浏览次数:14  
标签:HDU int Day4 cin 2024 ++ -- include

T1001 超维攻坚(HDU 7469)

三维凸包,不会。

T1002 黑白边游戏(HDU 7470)

显然这道题没有一个固定的最优策略,所以只能 \(\text{dp}\) 决策。

可以倒着做,设 \(f_{i, S}\) 表示从后往前进行了第 \(i \sim m\) 轮,第 \(i\) 轮结束后(在原始意义下是开始前)黑边集合为 \(S\),双方使用最优策略时先手与后手的分差,\(g_{S, a, b}\) 表示黑边集合为 \(S\),一条黑边/白边长度分别为 \(a, b\) 时,可以获得的收益(就是两两最短路之和)。

转移考虑枚举 \(T\),需要满足 \(S\) 可翻转一条边的颜色到达 \(T\),那么 \(f_{i, S} = \max \{ g_{a_i, b_i, T} - f_{i + 1, T} \}\)。注意到 \(S\) 是 \(O(2^{\frac{n(n - 1)}{2}})\) 的,无法接受。

注意到这题点与点之间是不区分的,我们可以将本质不同的图全部搜出来,建立状态自动机,在自动机上跑上述 \(\text{dp}\)(只需将 \(S\) 换成自动机上的结点即可),我们猜测自动机上的结点不会太多,实际上,在 \(n = 8\) 时只有 \(12346\) 个结点。

现在考虑如何建出自动机,也就是找出所有本质不同的图(现在我们默认删掉所有白边,那么我们只考虑有边和无边),并在恰好相差一条边的图之间连边。

对于一张图 \(G\),我们钦定它的最小表示为,对于所有结点,计算它的度数 \(d\),和所有邻居度数组成的可重集 \(d'\),对于所有点按二元组 \((d, d')\) 从小到大排序,然后按照 \((d, d')\) 划分等价类,对于同一个等价类中的点,我们直接阶乘枚举全排列,最后考虑重标号后的邻接矩阵,我们取字典序最小的邻接矩阵作为图的最小表示。然后我们认为两张图 \(G_0, G_1\) 同构,当且仅当他们的最小表示相等。

知道如何判断图的同构后,我们对于所有图按照 \(|E|\) 分层,容易发现只有相邻层之间有连边,所以考虑从当前层 \(|E| = e\) 转移到下一层 \(|E| = e + 1\),枚举当前层的一张图,在它上面任加一条边,然后判断一下这张新图是否已经有了即可。

复杂度可能不太好分析,但是我们有自动机上点数最大为 \(12346\),转移数最大为 \(172844\)。

Code
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <map>
#include <unordered_set>
#include <chrono>
#include <array>

using namespace std;
using uLL = unsigned long long;

uLL Get_time () {
	return chrono::steady_clock::now().time_since_epoch().count();
}

namespace State_automaton {
	int n;

	struct G {
		struct State {
			uLL es;

			bool Get (int i, int j) {
				return ((es >> (i << 3)) >> j) & 1; 
			}

			void Add (int i, int j) {
				es |= (1ull << (((i << 3) | j)));
				es |= (1ull << (((j << 3) | i)));
			}

			State () : es(0) {}
			State (uLL _es) : es(_es) {}

			void Rebuild () {
				vector<int> deg(n);
				vector<uLL> cdeg(n);
				for (int i = 0; i < n; ++i) {
					for (int j = i + 1; j < n; ++j) {
						if (Get(i, j)) ++deg[i], ++deg[j];
					}
				}
				for (int i = 0; i < n; ++i) {
					for (int j = 0; j < n; ++j) {
						if (Get(i, j)) {
							cdeg[i] += 1ull << (deg[j] * 3);
						}
					}
				}
				auto cmp = [&](int i, int j) -> bool {
					return deg[i] < deg[j] || deg[i] == deg[j] && cdeg[i] < cdeg[j];
				};
				vector<int> p(n);
				iota(p.begin(), p.end(), 0);
				sort(p.begin(), p.end(), cmp);
				vector<int> rk(n);
				rk[0] = 0; 
				for (int i = 1; i < n; ++i) {
					rk[p[i]] = rk[p[i - 1]] + cmp(p[i - 1], p[i]);
				}
				int tot = rk[p[n - 1]] + 1; 
				vector<vector<int>> vec(tot);
				for (int i = 0; i < n; ++i) {
					vec[rk[i]].push_back(i);
				}
				uLL mi = -1;
				auto Dfs = [&](auto &self, int x) -> void {
					if (x == tot) {
						vector<int> per;
						for (int i = 0; i < tot; ++i) {
							for (auto j : vec[i]) {
								per.push_back(j);
							}
						}
						uLL news = 0;
						for (int i = n - 1; ~i; --i) {
							for (int j = n - 1; j > i; --j) {
								if (Get(per[i], per[j])) {
									news |= (1ull << (((i << 3) | j)));
									news |= (1ull << (((j << 3) | i)));
								}
							}
						}
						mi = min(mi, news);
						return; 
					}
					sort(vec[x].begin(), vec[x].end());
					do {
						self(self, x + 1); 
					} while (next_permutation(vec[x].begin(), vec[x].end()));
				};
				Dfs(Dfs, 0);
				es = mi;
			}
		};

		int tot;
		vector<unordered_set<int>> e; 
		vector<State> s;
		vector<array<array<int, 5>, 5>> g;		

		int New_Node (State a) {
			e.push_back(unordered_set<int>());
			s.push_back(a);
			g.push_back({});
			vector<vector<int>> dis(n, vector<int>(n));
			for (int i = 0; i < 5; ++i) {
				for (int j = 0; j < 5; ++j) {
					g.back()[i][j] = 0; 
					for (int x = 0; x < n; ++x) {
						for (int y = 0; y < n; ++y) {
							dis[x][y] = (x == y ? 0 : (a.Get(x, y) ? i + 1 : j + 1));
						}
					}
					for (int z = 0; z < n; ++z) {
						for (int x = 0; x < n; ++x) {
							for (int y = 0; y < n; ++y) {
								dis[x][y] = min(dis[x][y], dis[x][z] + dis[z][y]);
							}
						} 
					}
					for (int x = 0; x < n; ++x) {
						for (int y = 0; y < n; ++y) {
							g.back()[i][j] += dis[x][y];
						}
					}
				}
			}
			return tot++;
		}

		void Add_edge (int u, int v) {
			e[u].insert(v);
			e[v].insert(u);
		}

		void Init () {
			vector<pair<State, int>> now{{State(), New_Node(State())}};
			for (int k = 1; k <= n * (n - 1) / 2; ++k) {
				map<uLL, int> p;
				for (auto i : now) {
					State s = i.first;
					int id = i.second;
					for (int x = 0; x < n; ++x) {
						for (int y = x + 1; y < n; ++y) {
							if (!s.Get(x, y)) {
								State t = s;
								t.Add(x, y);
								t.Rebuild();
								auto it = p.find(t.es);
								if (it != p.end()) {
									Add_edge(id, it->second);
								}
								else {
									it = p.insert({t.es, New_Node(t)}).first;
									Add_edge(id, it->second);
								}
							}
						}
					}
				}
				now.clear();
				for (auto i : p) {
					now.push_back(pair<State, int>({State({i.first}), i.second}));
				}
			}
		}
	} g[7];

	void Init () {
		for (int i = 0; i < 7; ++i) {
			n = i + 2, g[i].Init();
		}
	}
}

const int M = 305, S = 12346;
const int Inf = 1e9;

int n, m;
int f[M][S];
int a[M], b[M];

int main () {
	cin.tie(0)->sync_with_stdio(0);
	uLL st0 = Get_time();
	State_automaton::Init();
	int T;
	cin >> T;
	while (T--) {
		cin >> n >> m;
		auto &mg = State_automaton::g[n - 2];
		for (int i = 1; i <= m; ++i) {
			cin >> a[i] >> b[i];
			--a[i], --b[i];
		}
		for (int i = 0; i < mg.tot; ++i) {
			f[m + 1][i] = 0; 
		}
		for (int i = m; i; --i) {
			for (int s = 0; s < mg.tot; ++s) {
				f[i][s] = -Inf;
				for (auto t : mg.e[s]) {
					f[i][s] = max(f[i][s], mg.g[t][a[i]][b[i]] - f[i + 1][t]);
				}
			}
		}
		cout << f[1][mg.tot - 1] << '\n';
	}
	uLL ed0 = Get_time();
	// cerr << "Time0 = " << (ed0 - st0) / int(1e6) << '\n';
	return 0; 
}

T1003 最优 K 子段(HDU 7471)

首先可以二分,转化成判断是否存在不相交的 \(k\) 个长度为质数的子段,满足这 \(k\) 段的和都 \(\ge x\)。

考虑贪心,每次我们在合法的子段中选一个最靠左的肯定不劣。所以从左往右扫一遍,相当于每次将 \(r \gets r + 1\),你需要判断是否存在 \(l \in [minl, r]\) 使得子段 \([l, r]\) 合法。有 \(minl\) 的限制是因为不能和之前的子段重合。

做前缀和 \(s_i = \sum\limits_{k = 1}^{i} a_k\),那么一个子段中所有数的和可以表示为 \(s_r - s_{l - 1}\),容易发现如果没有子段长度为质数的限制是好做的,因为我们固定右端点 \(r\) 后,必定会选使得 \(s_{l - 1}\) 最小的 \(l\)。但加入质数的限制后这样可能会使 \([l, r]\) 的长度不为质数,所以只记一个决策点肯定是不行的。

考虑用 \(\text{set}\) 按照 \(s_{l - 1}\) 从小到大维护所有决策点 \(l\),查询一个 \(r\) 时在 \(\text{set}\) 上暴力跳,由于素数密度,期望跳 \(O(\log n)\) 次就可以跳到一个质数。注意这个 \(\log\) 是素数密度,而不是 \(s\) 的单调栈期望长度,后者是假的。

时间复杂度 \(O(n \log n \log V)\)。

Code
#include <iostream>
#include <vector>
#include <set>

using namespace std;
using Pii = pair<int, int>; 

const int N = 2e5 + 5;
const int Inf = 1e9;

int n, k;
int a[N], pre[N];
bool is_prime[N];
vector<int> prime;

bool check (int x) {
  set<Pii> s;
  int cnt = 0;
  for (int i = 1; i <= n; ++i) {
    int maxv = [&]() -> int {
      for (auto j : s) {
        if (is_prime[i - j.second]) {
          return pre[i] - j.first;
        }
      }
      return -Inf;
    }();
    s.insert({pre[i - 1], i - 1});
    if (maxv >= x) {
      ++cnt;
      s.clear();
    }
  }
  return cnt >= k;
} 

int main () {
  ios::sync_with_stdio(0);
  cin.tie(0); cout.tie(0);
  fill(is_prime + 2, is_prime + N, 1);
  for (int i = 2; i < N; ++i) {
    if (is_prime[i]) {
      for (int j = i * 2; j < N; j += i) {
        is_prime[j] = 0;
      }
    }
  }
  int T;
  cin >> T;
  while (T--) {
    cin >> n >> k;
    for (int i = 1; i <= n; ++i) {
      cin >> a[i], pre[i] = pre[i - 1] + a[i];
    }
    const int low = -2e3, hig = 2e8;
    int ansl = low, ansr = hig;
    while (ansl <= ansr) {
      int ansm = (ansl + ansr) >> 1;
      if (check(ansm)) {
        ansl = ansm + 1;
      }
      else {
        ansr = ansm - 1;
      }
    }
    if (ansr == low - 1) {
      cout << "impossible" << '\n';
    } 
    else {
      cout << ansr << '\n';
    }
  }
  return 0; 
}

T1004 分组(HDU 7472)

考虑把四个集合分成两部分,每部分两个集合,分别算一个答案,然后用一些优化技巧拼起来,大概相当于 Meet in the middle。

设 \(f_{S, v}\) 表示我们划分出一个部分 \(S\),是否能在内部选出一个异或和为 \(v\) 的集合,直接转移即可。

枚举 \(L \cup R = [1, n]\),表示划分的两个部分,对于部分 \(L\),通过 \(f\) 数组可以知道是否可以进一步划分出两个集合,使它们的异或和分别为 \(A, B\),对于 \(R\) 类似,有许多 \(C, D\) 可供选择,直接这样暴力做是 \(O(2^{n + 2m})\) 的。

不妨钦定 \(w_A \ge w_B, w_C \ge w_D\),这样一种方案的代价就是 \(\max(w_A, w_C) - \min(w_B, w_D)\),枚举 \(A\) 并分类讨论:

  • 对于 \(w_A \ge w_C\),答案为 \(w_A - \min(w_B, w_D)\) 显然我们只需知道最大的 \(w_D\),设 \(s_i\) 表示 \(w_C = i\) 时的最大 \(w_D\),我们所查询的相当于 \(s\) 的一段前缀 \(\max\)。
  • 对于 \(w_A < w_C\) 类似。

我们可以对 \(w\) 排序,然后前缀优化一下即可。

时间复杂度 \(O(2^{n + m})\)。

Code
#include <iostream>
#include <algorithm>
#include <bitset>

using namespace std;

const int N = 18, NS = (1 << N);
const int M = 10, MS = (1 << M);
const int Inf = 1e9;

int n, m; 
int a[N], w[MS], p[MS], rk[MS];
int s[MS], pres[MS], t[MS], pret[MS];
bitset<MS> f[NS];

int main () {
	cin.tie(0)->sync_with_stdio(0);
	int T;
	cin >> T;
	while (T--) {
		cin >> n >> m;
		for (int i = 0; i < n; ++i) {
			cin >> a[i];
		}
		for (int i = 0; i < (1 << m); ++i) {
			cin >> w[i], p[i] = i; 
		} 
		sort(p, p + (1 << m), [&](int i, int j) -> bool {
			return w[i] < w[j];
		});
		for (int i = 0; i < (1 << m); ++i) {
			rk[p[i]] = i; 
		}
		for (int s = 0; s < (1 << n); ++s) {
			f[s].reset();
		}
		f[0][0] = 1; 
		for (int s = 1; s < (1 << n); ++s) {
			int x = -1;
			for (int i = 0; i < n; ++i) {
				if ((s >> i) & 1) {
					x = i;
					break;
				}
			}
			int t = s ^ (1 << x);
			for (int i = 0; i < (1 << m); ++i) {
				if (f[t][i]) {
					f[s][i] = 1, f[s][i ^ a[x]] = 1; 
				}
			}
		}
		int ans = Inf;
		for (int L = 0; L < (1 << n); L += 2) {
			int R = ((1 << n) - 1) ^ L, Ls = 0, Rs = 0; 
			for (int i = 0; i < n; ++i) {
				((L >> i) & 1 ? Ls : Rs) ^= a[i]; 
			}
			fill(s, s + (1 << m), -1);
			fill(t, t + (1 << m), -1);
			fill(pres, pres + (1 << m), -1);
			fill(pret, pret + (1 << m), -1);
			for (int wa = 0; wa < (1 << m); ++wa) {
				if (!f[L][wa]) continue;
				int wb = Ls ^ wa;
				if (rk[wb] > rk[wa]) continue;
				s[rk[wa]] = max(s[rk[wa]], rk[wb]);
			}
			for (int wc = 0; wc < (1 << m); ++wc) {
				if (!f[R][wc]) continue;
				int wd = Rs ^ wc;
				if (rk[wd] > rk[wc]) continue;
				t[rk[wc]] = max(t[rk[wc]], rk[wd]);
			}
			pres[0] = s[0], pret[0] = t[0];
			for (int i = 1; i < (1 << m); ++i) {
				pres[i] = max(s[i], pres[i - 1]);
				pret[i] = max(t[i], pret[i - 1]);
			}
			for (int i = 0; i < (1 << m); ++i) {
				if (s[i] != -1 && pret[i] != -1) {
					ans = min(ans, w[p[i]] - w[p[min(s[i], pret[i])]]);
				}
				if (t[i] != -1 && pres[i] != -1) {
					ans = min(ans, w[p[i]] - w[p[min(t[i], pres[i])]]);
				}
			}
		}
		cout << ans << '\n';
	}
	return 0; 
}

T1005 多层血条(HDU 7473)

注意到只有在 \([hp - m + 1, hp]\) 范围内的生命值是有用的,其余都会被覆盖掉,暴力处理这一段即可。

Code
#include <iostream>
#include <cassert>

using namespace std;

int main () {
  // freopen("1005.in", "r", stdin);
  // freopen("1005.out", "w", stdout);
  ios::sync_with_stdio(0);
  cin.tie(0); cout.tie(0);
  int T;
  cin >> T;
  while (T--) {
    int n, m, hp, dmg;
    cin >> n >> m >> hp >> dmg; 
    cout << '+' << string(m, '-') << '+' << '\n';
    string ans = string(m, ' ');
    for (int i = max(1, hp - m + 1); i <= hp; ++i) {
      int pos = (i - 1) % m;
      char &res = ans[pos];
      int cur = (i - 1) / m + 1;
      char ch = 'A' + ((cur - 1) % 5);
      res = (i >= hp - dmg + 1 ? '.' : ch);
    }
    assert(int(ans.size()) == m);
    for (int i = 1; i <= n; ++i) {
      cout << '|' << ans << '|' << '\n';
    }
    cout << '+' << string(m, '-') << '+' << '\n';
  }
  return 0; 
}

T1006 延时操控(HDU 7474)

首先延时移动显然不好做,考虑将敌方的操作提前 \(k\) 回合,不影响答案,相当于我们要在 \(m - k\) 回合打败敌方,然后接着随机游走 \(k\) 步,我们将这两部分分开计算。

注意到敌方会和己方在同一个方向移动,除非它受到了伤害。由于它受到的伤害不超过 \(hp\),所以它与己方相对位置的变化量(较初始的相对位置而言)不超过 \(hp\),所以设 \(f_{i, x_1, y_1, tx, ty, d}\) 表示经过 \(i\) 个回合,我方位于 \((x_1, y_1)\),我方与敌方相对位置变化量为 \(tx, ty\),敌方受到了 \(d\) 点伤害的操作序列数。

设 \(g_{x_1, y_1, k}\) 表示从 \((x_1, y_1)\) 开始游走 \(k\) 步且不能碰到障碍的方案数,计算答案 \(f, g\) 拼一下即可。

时间复杂度 \(O(mn^2hp^3)\)。

Code
#include <iostream>
#include <cstring>

using namespace std;

const int N = 53;
const int Mod = 1e9 + 7; 

const int Dx[4] = {0, 0, 1, -1};
const int Dy[4] = {1, -1, 0, 0};

int n, m, t, hp, px, py, ex, ey, dx, dy;
char s[N][N];
int f[N][N][N][11][11][6], g[N][N][N];

int main () {
    cin.tie(0)->sync_with_stdio(0);
    int T;
    cin >> T;
    while (T--) {
        cin >> n >> m >> t >> hp, m -= t;
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                cin >> s[i][j];
                if (s[i][j] == 'P')
                    px = i, py = j; 
                if (s[i][j] == 'E')
                    ex = i, ey = j; 
            }
        }
        for (int i = 0; i <= n + 1; ++i) {
            for (int j = 0; j <= n + 1; ++j) {
                if (!i || !j || i == n + 1 || j == n + 1)  
                    s[i][j] = '#';
            }
        }
        dx = px - ex, dy = py - ey;
        fill(f[0][0][0][0][0], f[m][n][n][hp * 2 + 1][hp * 2 + 1] + hp + 1, 0);
        f[0][px][py][hp][hp][0] = 1; 
        for (int i = 1; i <= m; ++i) {
            for (int x1 = 1; x1 <= n; ++x1) {
                for (int y1 = 1; y1 <= n; ++y1) {
                    for (int d = 0; d < hp; ++d) {
                        for (int tx = -d + hp; tx <= d + hp; ++tx) {
                            for (int ty = -d + hp; ty <= d + hp; ++ty) {
                                int x2 = x1 - dx - (tx - hp), y2 = y1 - dy - (ty - hp); 
                                if (x2 <= 0 || x2 > n || y2 <= 0 || y2 > n) continue;
                                int lstv = f[i - 1][x1][y1][tx][ty][d];
                                if (!lstv) continue; 
                                for (int k = 0; k < 4; ++k) {
                                    int _x1 = x1 + Dx[k];
                                    int _y1 = y1 + Dy[k];
                                    if (s[_x1][_y1] == '#') continue;
                                    int _x2 = x2 + Dx[k];
                                    int _y2 = y2 + Dy[k];
                                    int _d = d;
                                    if (s[_x2][_y2] == '#') 
                                        _x2 = x2, _y2 = y2, ++_d;
                                    int _tx = (_x1 - _x2) - dx + hp, _ty = (_y1 - _y2) - dy + hp; 
                                    f[i][_x1][_y1][_tx][_ty][_d] = (f[i][_x1][_y1][_tx][_ty][_d] + lstv) % Mod;
                                }
                            }
                        }
                    }
                }
            }
        }
        memset(g, 0, sizeof(g));
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (s[i][j] != '#')
                    g[i][j][0] = 1;
            }
        }
        for (int k = 1; k <= t; ++k) {
            for (int i = 1; i <= n; ++i) {
                for (int j = 1; j <= n; ++j) {
                    for (int d = 0; d < 4; ++d) {
                        int x = i + Dx[d];
                        int y = j + Dy[d];
                        if (s[x][y] != '#')
                            g[i][j][k] = (g[i][j][k] + g[x][y][k - 1]) % Mod;
                    }
                }
            }
        }
        int ans = 0;
        for (int i = 1; i <= m; ++i) {
            for (int x1 = 1; x1 <= n; ++x1) {
                for (int y1 = 1; y1 <= n; ++y1) {
                    for (int tx = 0; tx <= hp * 2; ++tx) {
                        for (int ty = 0; ty <= hp * 2; ++ty) {
                            ans = (ans + 1ll * f[i][x1][y1][tx][ty][hp] * g[x1][y1][t]) % Mod;
                        }
                    }
                }
            }
        }
        cout << ans << '\n';
    }
    return 0; 
}

T1007 序列更新(HDU 7475)

考虑对于每个位置独立算答案。每个位置暴力做 \(\sqrt n\) 次操作,做完这些操作后期望只有 \(O(\sqrt n)\) 种数字大于当前最大值,算这些数字最早什么时候对这个位置有贡献即可。

时间复杂度 \(O(n \sqrt n)\)。

Code
#include <iostream>
#include <numeric>
#include <algorithm>
#include <cmath>

using namespace std;
using LL = long long;

const int N = 2.5e5 + 5;

int n, m;
int a[N], b[N], ok[N], fs[N], p[N];
LL ans[N];

int main () {
  // freopen("1007.in", "r", stdin);
  // freopen("1007.out", "w", stdout);
  ios::sync_with_stdio(0);
  cin.tie(0); cout.tie(0);
  int T;
  cin >> T;
  while (T--) {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
      cin >> a[i];
    }
    for (int i = 1; i <= n; ++i) {
      cin >> b[i];
    }
    iota(p + 1, p + n + 1, 1);
    auto cmp = [&](int i, int j) -> bool {
      return b[i] < b[j];
    };
    sort(p + 1, p + n + 1, cmp);
    fill(fs, fs + n, n + 1);
    for (int i = 1; i <= m; ++i) {
      cin >> ok[i], fs[ok[i]] = min(fs[ok[i]], i);
    }
    fill(ans, ans + m + 1, 0);
    auto add = [&](int l, int r, int x) -> void {
      ans[l] += x, ans[r + 1] -= x;
    };
    int d = min(int(sqrt(n)), m);
    for (int i = 1; i <= n; ++i) {
      int now = a[i];
      for (int j = 1; j <= d; ++j) {
        now = max(now, b[(i + ok[j] - 1) % n + 1]);
        add(j, j, now);
      }
      auto cmp = [&](int i, int j) -> bool {
        return b[i] < j;
      };
      int low = lower_bound(p + 1, p + n + 1, now, cmp) - p, ed = n;
      for (int j = n; j >= low; --j) {
        int id = p[j];
        int abl = max(d + 1, fs[(id - i + n) % n]);
        if (abl <= ed) {
          add(abl, ed, b[id]);
          ed = abl - 1;
        }
      }
      add(d + 1, ed, now);
    }
    for (int i = 1; i <= m; ++i) {
      ans[i] += ans[i - 1];
      cout << ans[i] << '\n';
    }
  }
  return 0;
}

T1008 魔法卡牌(HDU 7476)

不会。

T1009 昵称检索(HDU 7477)

对于每一个昵称算包含它的最短前缀 \([1, x]\),对于每一个日期算包含它的最短后缀 \([y, n]\),一对昵称-日期会产生 \(1\) 的贡献当且仅当 \(x < y\),做一个前缀和即可快速计算。

时间复杂度 \(O(n + m + \sum |name|)\)。

Code
#include <iostream>

using namespace std;

const int N = 1e6 + 5; 

int n, m;
int la[N][10], nx[N][26], suf[N];
string s;

int main () {
  // freopen("1009.in", "r", stdin);
  // freopen("1009.out", "w", stdout);
  ios::sync_with_stdio(0);
  cin.tie(0); cout.tie(0);
  int T;
  cin >> T;
  while (T--) {
    cin >> m >> n >> s, s = " " + s;
    for (int i = 1; i <= n; ++i) {
      copy(la[i - 1], la[i - 1] + 10, la[i]);
      if (s[i] >= '0' && s[i] <= '9') {
        la[i][s[i] - '0'] = i;
      }
    }
    const int days[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    auto get_string = [&](int x) -> string {
      return to_string(x / 10) + to_string(x % 10); 
    };
    fill(suf, suf + n + 2, 0);
    for (int month = 1; month <= 12; ++month) {
      for (int day = 1; day <= days[month - 1]; ++day) {
        string goal = get_string(month) + get_string(day);
        int pos = n + 1; 
        for (int i = 3; pos && ~i; --i) {
          int x = goal[i] - '0';
          pos = la[pos - 1][x];
        }
        ++suf[pos];
      }
    }
    for (int i = n; i; --i) {
      suf[i] += suf[i + 1];
    }
    fill(nx[n + 1], nx[n + 1] + 26, n + 1);
    for (int i = n; i; --i) {
      copy(nx[i + 1], nx[i + 1] + 26, nx[i]);
      if (s[i] >= 'a' && s[i] <= 'z') {
        nx[i][s[i] - 'a'] = i;
      }
    }
    int ans = 0; 
    for (string s; m--; ) {
      cin >> s;
      int pos = 0; 
      for (auto c : s) {
        int x = c - 'a';
        pos = nx[pos + 1][x];
        if (pos >= n) break;
      }
      if (pos + 1 <= n) ans += suf[pos + 1];
    }
    cout << ans << '\n';
  }
  return 0;
}

T1010 矩阵的周期(HDU 7478)

不会。

T1011 找环(HDU 7479)

不会。

T1012 寻找宝藏(HDU 7480)

设 \(f_i\) 表示从 \((0, 0)\) 走到第 \(i\) 个宝藏的位置,能获得的最大收益,\(g_i\) 表示从 \((n + 1, n + 1)\) 走到第 \(i\) 个宝箱的位置,能获得的最大收益,这个都容易用树状数组优化求出。

考虑一个禁区矩形 \([x_1, x_2] \times [y_1, y_2]\),答案有可能是来自以下四种情况:

  • 选一个坐标为 \([1, x_1) \times (y_2, n]\) 的点 \(A\),答案为 \(f_A + g_A - v_A\)。

  • 选一个坐标为 \((x_2, n] \times [1, y_1)\) 的点 \(A\),答案为 \(f_A + g_A - v_A\)。

  • 选一个坐标为 \([1, x_1) \times [1, y_2]\) 的点 \(A\),再选一个坐标为 \([x_1, n] \times (y_2, n]\) 的点 \(B\),答案为 \(f_A + g_B\)。

  • 选一个坐标为 \([1, x_2] \times [1, y_1)\) 的点 \(A\),再选一个坐标为 \((x_2, n] \times [y_1, n]\) 的点 \(B\),答案为 \(f_A + g_B\)。

注意到后两种情况 \(A\) 和 \(B\) 的选取是独立的,所以还是相当于二位数点问题,扫描线 + 树状数组即可。

时间复杂度 \(O(n \log n)\)。

Code
#include <iostream>
#include <vector>

using namespace std;
using LL = long long;

const int N = 3e5 + 5;

int n, m;
int p[N], v[N];
LL f[N], g[N], c[N], ans[N], sum1[N], sum2[N];
int x1[N], x2[N], y1[N], y2[N];
vector<int> vx1[N], vx2[N];

void Insert (int x, LL y) {
    for (; x <= n; x += (x & -x)) {
        c[x] = max(c[x], y);
    }
}

LL Query (int x) {
    LL res = 0;
    for (; x; x -= (x & -x)) {
        res = max(res, c[x]);
    }
    return res;
}

int main () {
    cin.tie(0)->sync_with_stdio(0);
    int T;
    cin >> T;
    while (T--) {
        cin >> n >> m; 
        for (int i = 1; i <= n; ++i) {
            cin >> p[i] >> v[i];
        }
        fill(c, c + n + 1, 0);
        for (int i = 1; i <= n; ++i) {
            f[i] = Query(p[i]) + v[i];
            Insert(p[i], f[i]);
        } 
         fill(c, c + n + 1, 0);
        for (int i = n; i; --i) {
            g[i] = Query(n - p[i] + 1) + v[i];
            Insert(n - p[i] + 1, g[i]);
        }
        fill(vx1 + 1, vx1 + n + 1, vector<int>());
        fill(vx2 + 1, vx2 + n + 1, vector<int>());
        for (int i = 1; i <= m; ++i) {
            cin >> x1[i] >> y1[i] >> x2[i] >> y2[i];
            vx1[x1[i]].push_back(i);
            vx2[x2[i]].push_back(i);
        }
        fill(ans + 1, ans + m + 1, 0);
        fill(sum1 + 1, sum1 + m + 1, 0);
        fill(sum2 + 1, sum2 + m + 1, 0);
        fill(c, c + n + 1, 0);
        for (int i = 1; i <= n; ++i) {
            for (auto j : vx1[i]) {                
                ans[j] = max(ans[j], Query(n - y2[j]));
            }
            Insert(n - p[i] + 1, f[i] + g[i] - v[i]);
        }
        fill(c, c + n + 1, 0);
        for (int i = n; i; --i) {
            for (auto j : vx2[i]) {
                ans[j] = max(ans[j], Query(y1[j] - 1));
            }
            Insert(p[i], f[i] + g[i] - v[i]);
        }
        fill(c, c + n + 1, 0);
        for (int i = 1; i <= n; ++i) {
            for (auto j : vx1[i]) {
                sum1[j] += Query(y2[j]);
            }
            Insert(p[i], f[i]);
        }
        fill(c, c + n + 1, 0);
        for (int i = n; i; --i) {
            Insert(n - p[i] + 1, g[i]);
            for (auto j : vx1[i]) {
                sum1[j] += Query(n - y2[j]);
            }
        }
        fill(c, c + n + 1, 0);
        for (int i = 1; i <= n; ++i) {
            Insert(p[i], f[i]);
            for (auto j : vx2[i]) {
                sum2[j] += Query(y1[j] - 1);
            }
        }
        fill(c, c + n + 1, 0);
        for (int i = n; i; --i) {
            for (auto j : vx2[i]) {
                sum2[j] += Query(n - y1[j] + 1);
            }
            Insert(n - p[i] + 1, g[i]);
        }
        for (int i = 1; i <= m; ++i) {
            cout << max(ans[i], max(sum1[i], sum2[i])) << '\n';
        }
    }
    return 0;
}

标签:HDU,int,Day4,cin,2024,++,--,include
From: https://www.cnblogs.com/hztmax0/p/18363587

相关文章

  • FINC3600 Finance in Practice S2 2024
    FINC3600 Financein PracticeS2 2024Project 1:Corporate Finance BriefProject LearningObjectivesBycompletingthisassignment,you will:Learn to apply corporate finance concepts and techniques from past finance courses in a realisti......
  • 2024牛客多校第9场
    9BBreakSequence(B)似乎不止一次遇到线段树优化dp了,但仍然没做出来()\(dp[i]\)表示到\(i\)位置为止、将序列分成若干段的情况总数,一个显而易见的\(n^2\)做法是从\(1\)到\(i-1\)枚举\(j\),若\(j+1\)至\(i\)为合法区间,则有\(dp[i]=\sum\limitsdp[j]\).假......
  • 2024-08-17:用go语言,给定一个从0开始的整数数组nums和一个整数k, 每次操作可以删除数组
    2024-08-17:用go语言,给定一个从0开始的整数数组nums和一个整数k,每次操作可以删除数组中的最小元素。你的目标是通过这些操作,使得数组中的所有元素都大于或等于k。请计算出实现这个目标所需的最少操作次数。输入:nums=[2,11,10,1,3],k=10。输出:3。解释:第一次操作后,nums变......
  • P10703 [SNCPC2024] 窗花
    [SNCPC2024]窗花题目描述有一扇\(100\text{cm}\times100\text{cm}\)的窗户和\(n\)个对角线长为\(2\text{cm}\)的正方形窗花。建立坐标系,以窗户左下角的坐标为原点\((0,0)\),右上角坐标为\((100,100)\),第\(i\)个窗花中心被贴在非边缘的整坐标点\((x_i,y_i)\)(\(......
  • [2027届]NOIP2024模拟赛#3
    老规矩,先放榜。打的还行。T1一眼想到按照字典序排序。然后想到了同学slay.one的号AAaz,于是想看看aaaz和aaa的顺序。不试不知道,一试吓一跳,萎了。然后想到特判,发现bbba和bbb又萎了。然后一瞬间想到哈希看到排序的恶心程度很像之前的一个ABC-F。突然发现按照那道......
  • yolo入门 yolov8下载安装--2024.8
    默认已安装Anaconda(一个类似于环境管理器的软件,前面出过anaconda安装教程)1.创建激活环境打开AnacondaPrompt,创建yolov8环境condacreate-nyolov8python=3.8激活环境activateyolov82.下载yolov8安装包 下载链接:https://github.com/ultralytics/ultralytics同时可......
  • 随记 - 2024 年 4 月 12 日
    写在前面444字|生活|经历|感触正文或许因为压力大,亦或者简单的糖分不足,今晚好想吃面包和蛋糕。蛋糕吃不完也买不起,面包还是可以。实在饿,出门了。导航两家西点店,关门。怏怏地找另一家。在十点前,找到了。怡朵。还在营业。登门,看到货架基本都......
  • 2024杭电多校第九场
    1007简单博弈,队友做的#include<bits/stdc++.h>usingnamespacestd;constintN=2e5;intn,a[N+5],b[N+5],A,B;boolvis[N+5];inlineintread(){intx=0;boolf=1;charch=getchar();for(;ch<'0'||ch>'9';ch=getchar())f^=(ch==......
  • 2024.8.16随笔(补)
    前言本来准备熬夜补的,结果因为前一天熬了夜加上家长压力被迫正常睡觉。讲课上午是whx给我们讲莫反和筛法。前半部分我全部听懂了,后面我听懂了但是印象不深刻,遇到题自己也推不出来。总的来说听懂了80%以上。重点聊筛法吧,就前面的简单的筛法之前讲数论讲过很简单,然后杜教筛当时......
  • 2024新型数字政府综合解决方案(一)
    新型数字政府综合解决方案通过整合先进的数字技术和智能化系统,构建了一个高效、透明且响应迅速的政府服务平台,能够实现跨部门数据共享和实时信息更新。该解决方案包括智能数据分析、大数据平台和云计算服务,旨在提升政府决策的科学性和行政管理的效能。通过在线服务和自动化流程......