Maximum Remaining
题意:给n个数,取出两个数$a_{i}$,$a_{j}$,求$a_{i}\% a_{j}$取模的最大值
直接排个序,第二大(严格的第二大)模第一大就是答案了。
#includeusing namespace std; int a[(int)1e6]; int main() { ios::sync_with_stdio(false); cin.tie(0); int n; cin >> n; for (int i = 0; i < n; i++) cin >> a[i]; sort(a, a + n, greater<int>()); for (int i = 1; i < n; i++) if (a[i] != a[0]) { cout << a[i] % a[0] << '\n'; return 0; } cout << "0" << '\n'; return 0; }
Friend or Girlfriend
题意:给一个字符串还有一个字母,问字符串里有多少个子串是这个字母
对于字符串 abcbcafdcde 给定字母为c
首先每到一个位置i 如果s[i]是c 答案就加上这个位置 表示以s[i]为结尾的子串
比如到s[3]是c 答案就加3 分别是abc bc c
到了s[5] 答案加上5 明显不止这么少
因为中间的b可以作为子串结尾 然后以位置3以前的任意一个字母为起点都是一个合法的子串
所以记录一下last的位置 ans += (i - last - 1) * last 就好了
因为最后一个位置不是给定字母的话不会被统计进去 特判一下就好了
#includeusing namespace std; char s[(int)1e6 + 10]; int main() { int T; scanf("%d", &T); while (T--) { int n; scanf("%d", &n); scanf("%s", s + 1); char x[3]; scanf("%s", x); long long ans = 0; int last = 0; for (int i = 1; i <= n; i++) { if (s[i] == x[0]) { ans += (long long)i; if (i - last - 1 != 0) { ans += (long long)(i - last - 1) * last; } last = i; } if (i == n && s[i] != x[0]) { ans += (long long)(i - last) * last; } } printf("%lld\n", ans); } }
Fencing
题意:给一个$N\times M$的矩阵,有 K 格是菜,求最少用多长的篱笆能把这些菜围起来(不能有边与边界或杂草相连)N,M都是1e9
最多肯定就是4 * K啦。然后连通的两个菜格子答案就-2
我是用了结构体和map来映射一个点的下标 然后dfs搜K个格子 只搜右和下两个方向 否则就会因为环跑不出来或者答案统计不全
#includeusing namespace std; const int maxn = 1e5 + 10; int n, m, k, ans; struct Point { int x, y; bool operator < (const Point &a) const { if (x == a.x) return y < a.y; return x < a.x; } } p[maxn]; map int> mp; bool vis[maxn]; void dfs(int x, int y, int index) { vis[index] = true; Point temp; temp.x = x + 1, temp.y = y; if (mp.count(temp)) { ans -= 2; if (!vis[mp[temp]]) dfs(x + 1, y, mp[temp]); } temp.x = x, temp.y = y + 1; if (mp.count(temp)) { ans -= 2; if (!vis[mp[temp]]) dfs(x, y + 1, mp[temp]); } } int main() { int T; scanf("%d", &T); while (T--) { mp.clear(); memset(vis, 0, sizeof vis); scanf("%d%d", &n, &m); scanf("%d", &k); for (int i = 1; i <= k; i++){ scanf("%d%d", &p[i].x, &p[i].y); mp[p[i]] = i; } int index = 0; ans = 4 * k; for (int i = 1; i <= k; i++) { if (!vis[i]) dfs(p[i].x, p[i].y, i); } printf("%d\n", ans); } return 0; }
Subtree Removal
题意:给一棵带权的树和一个值X,可以执行k次操作,每次选择一个节点,删去它与它的子树,求剩下的节点权值和 - k * X
当时想的很复杂,不知道怎么写。
现在一想,其实是被这个k给限制住了,把k个X给拆出来,就相当于可以把k个节点包括它的子树的权值和给替换成 -X
这样想就是很简单了。(我好菜啊...
#include#define ll long long using namespace std; const int maxn = 1e5 + 10; vector<int> G[maxn]; int n; ll x; ll a[maxn]; ll f[maxn]; void dfs(int u, int fa) { f[u] = a[u]; for (int v : G[u]) { if (v == fa) continue; dfs(v, u); f[u] += f[v]; } f[u] = max(f[u], -x); } int main() { int T; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &x); // for (int i = 1; i <= n; i++) G[i].clear(); for (int i = 1; i <= n; i++) { G[i].clear(); scanf("%lld", &a[i]); // sum += 1LL * a[i]; } for (int i = 0; i < n - 1; i++) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } dfs(1, 0); printf("%lld\n", f[1]); } return 0; }
Playing with Numbers
题意:给一棵树,每个节点有一个权值还有一个模数,求每个叶子,从根到它的路上所有节点的权值的线性组合模上模数的最大值
$a_{1}$ $a_{2}$ $a_{3}$...$a_{n}$ 考虑一下它们的gcd 设为g
这n个数的线性组合本来是 $k_{1}a_{1}+k_{2}a_{2}+k_{3}a,+\ldots +k_{n}a_{n}$
全考虑成gcd后直接就变成了 k'g
所以说其实一些数的线性组合莫非就是它们的gcd的整数倍
现在就是直接看g和模数$m_{i}$
比如g = 4,模数为6 这样最大值就是2
g = 5,模数为7 最大值是6 (5 * 4 % 7 = 6)
多考虑几组就会发现答案就是$m_{i} - gcd(m_{i}, g)$
其实现在这个g,也可以进一步看成 $k_{1}g'$ g‘表示$m_{i}$和g的gcd $m_{i}$看成$k_{2}g'$
两个数相差$\Delta k$我们总有一个k可以让他们的$\Delta k$ = 1
所以就是边dfs边gcd 到叶子就统计答案就好了
#include#define ll long long using namespace std; const int maxn = 1e5 + 10; int n; vector<int> G[maxn]; int tol; ll a[maxn]; ll m[maxn]; pair<int, ll> ans[maxn]; inline void init() { for (int i = 1; i <= n; i++) G[i].clear(); tol = 0; } inline void addedge(int u, int v) { G[u].push_back(v); G[v].push_back(u); } ll gcd(ll a, ll b) { while (b) { a %= b; swap(a, b); } return a; } inline void get_ans(ll g, int index) { pair<int, ll> temp; temp.first = index; temp.second = m[index] - gcd(g, m[index]); ans[++tol] = temp; } void dfs(int u, int fa, ll g) { bool flag = false; for (int v : G[u]) { if (v == fa) continue; flag = true; dfs(v, u, gcd(g, a[v])); } if (!flag) get_ans(g, u); } int main() { int T; scanf("%d", &T); while (T--) { scanf("%d", &n); init(); for (int i = 0; i < n - 1; i++) { int u, v; scanf("%d%d", &u, &v); addedge(u, v); } for (int i = 1; i <= n; i++) scanf("%lld", &a[i]); for (int i = 1; i <= n; i++) scanf("%lld", &m[i]); dfs(1, 0, a[1]); sort(ans + 1, ans + 1 + tol); for (int i = 1; i <= tol; i++) { if (i != 1) putchar(' '); printf("%lld", ans[i].second); } puts(""); } return 0; }
Kira Loves Palindromes
题意:给一个字符串,求字符串的两个子串拼接起来后是回文串的方案数(两个子串不相交也不重复取)
区间dp
$dp_{ij}$表示第一个子串以$s_{i}$开头,第二个子串以$s_{j}$结尾符合的方案数
转移方程
$dp_{ij} = dp_{i+1,j-1} + f_{i + 1, j - 1} + b_{i + 1, j - 1} + 1$
其中$f_{ij}$表示以$s_{i}$为开头的回文串个数
$b_{ij}$表示以$s_{j}$为结尾的回文串个数
#include#define ll long long using namespace std; const int maxn = 1e3 + 10; bool ok[maxn][maxn]; int f[maxn][maxn], b[maxn][maxn]; ll dp[maxn][maxn], ans; char s[maxn]; int n; void pre() { for (int i = n - 1; i >= 0; i--) { for (int j = i; j < n; j++) { ok[i][j] = ((s[i] == s[j]) && (j - i < 3 || ok[i+1][j-1])); } } for (int i = 0; i < n; i++) f[i][i] = b[i][i] = 1; for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (ok[i][j]) f[i][j] = f[i][j-1] + 1; else f[i][j] = f[i][j-1]; } } for (int j = n - 1; j >= 0; j--) { for (int i = j - 1; i >= 0; i--) { b[i][j] = b[i+1][j]; if (ok[i][j]) b[i][j]++; } } } int main() { scanf("%s", s); n = strlen(s); pre(); for (int i = 0; i < n - 1; i++) { if (s[i] == s[i+1]) dp[i][i+1] = 1, ans++; } for (int l = 3; l <= n; l++) { for (int i = 0; i + l - 1 < n; i++) { int j = i + l - 1; if (s[i] == s[j]) { dp[i][j] = f[i+1][j-1] + b[i+1][j-1] + dp[i+1][j-1] + 1; ans += dp[i][j]; } } } printf("%lld\n", ans); return 0; }
Mininum XOR over Tree
可持久化字典树...待补...