给定 A,B 之间,B,C 之间,A,C 之间的不等关系(大于或小于),问 A,B,C 中排第二大的数是哪一个。
乍一看没啥思路,原来正解是打表(8 种情况秒了)。
if (a == "<" && b == "<" && c == "<") { cout << "B\n"; } else if (a == "<" && b == "<" && c == ">") { cout << "C\n"; } else if (a == "<" && b == ">" && c == "<") { cout << "B\n"; } else if (a == "<" && b == ">" && c == ">") { cout << "A\n"; } else if (a == ">" && b == "<" && c == "<") { cout << "A\n"; } else if (a == ">" && b == "<" && c == ">") { cout << "B\n"; } else if (a == ">" && b == ">" && c == "<") { cout << "C\n"; } else if (a == ">" && b == ">" && c == ">") { cout << "B\n"; }
某城依次有 m 个小孩出生,第 i 个小孩出生在第 A_i 个家庭,且有性别,问该小孩是不是该家庭的第一个儿子。
很简单,开一个标记数组来记录每一个家庭是否有过儿子,每次按照题意判断即可。
cin >> n >> m; for (int i = 1; i <= m; i ++) { cin >> a[i]; char c; cin >> c; if (c == 'F') { cout << "No\n"; continue; } if (!vis[a[i]]) cout << "Yes\n"; else cout << "No\n"; vis[a[i]] = true; }
给定两个 N\ (N\leq 8) 个节点的无向简单图 G,H,每次可以对图 H 进行一次操作 (i,j):
如果 H 含有边 (i,j),就将其删去,代价为 A_{i,j}。
如果 H 不含边 (i,j),就将其加上,代价为 A_{i,j}。
问通过这种操作,将两个图变成同构图的最小代价。
乍一看很可怕,原来可以枚举。从同构图的定义入手,先 O(n!) 来枚举点的对应关系。
每枚举一种对应关系,就去看邻接矩阵对应位置是否相等,不相等就进行操作。显然,操作之间相互独立。
#includeusing namespace std; typedef long long ll; ll n, mg, mh, A[10][10]; bool G[10][10], H[10][10]; ll res[10], used[10], ans = 1e18; void dfs(ll dep) { if (dep > n) { ll cost = 0; for (int i = 1; i <= n; i ++) for (int j = 1; j <= n; j ++) if (G[res[i]][res[j]] != H[i][j]) cost += A[i][j]; ans = min(ans, cost); return; } for (int i = 1; i <= n; i ++) { if (!used[i]) { used[i] = true; res[dep] = i; dfs(dep + 1); used[i] = false; } } } int main() { ios::sync_with_stdio(false); cin >> n >> mg; for (int i = 1; i <= mg; i ++) { ll u, v; cin >> u >> v; G[u][v] = G[v][u] = 1; } cin >> mh; for (int i = 1; i <= mh; i ++) { ll u, v; cin >> u >> v; H[u][v] = H[v][u] = 1; } for (int i = 1; i <= n; i ++) for (int j = i + 1; j <= n; j ++) cin >> A[i][j]; dfs(1); cout << ans << "\n"; return 0; }
现在有 N 条信息,每条信息形如:点 X_i 上有 P_i 个人。保证 X_i 单调递增。
现在有 Q 次询问,每次询问形如:L_i,R_i,问区间 [L_i,R_i] 当中的点上有多少个人。
因为 X_i 递增,所以可以二分。对于每次询问,用 lower_bound
和 upper_bound
求出 X 数组中位于区间内的所有点,然后用前缀和算一下 P 的区间和即可。
for (int i = 1; i <= n; i ++) s[i] = s[i-1] + p[i]; cin >> q; while (q--) { ll ql, qr; cin >> ql >> qr; ll first_idx = lower_bound(x + 1, x + n + 1, ql) - x; ll last_idx = upper_bound(x + 1, x + n + 1, qr) - x - 1; cout << s[last_idx] - s[first_idx - 1] << "\n"; }
定义 f(i,j) 表示 A_i,A_{i+1},\dots,A_r 当中不同的值的个数。求 \sum\limits_{i=1}^{n}\sum \limits_{j=i}^{n}f(i,j)。
非常经典的二维 \sum 问题。
枚举起点,把终点游标一格一格向右移动,每次加进来一个数,初始化标记数组为全 0,每遇到一个数,如果它的标记是 0,就是一个没出现过的数,答案加一,这样 O(n^2) 累加。
ll n, a[N]; bool vis[N]; ll ans = 0; for (int st = 1; st <= n; st ++) { for (int j = 1; j <= n; j ++) vis[j] = false; ll cnt = 0; for (int j = st; j <= n; j ++) { if (!vis[a[j]]) cnt ++; vis[a[j]] = true; ans += cnt; } } cout << ans << "\n";
跟暴力似乎没太大关系。因为值域是 [1,n],是很小的。对于一种数 i,记录它在数组中出现的所有位置 pt。
很显然,有多少个区间包含这个值 i,它对答案的贡献就是多少。下面问题转化为算出多少区间包含值 i。
假设有 m 个位置是 i,这 m 个位置的下标分别是 pt_1,pt_2,\dots,pt_m,那么那些被包含在两个位置中间空隙里的区间不包含 i。正难则反,一共有 \frac{n(n+1)}{2} 个区间,对于每个长度为 k 的“空隙“,其中会存在 \frac{k(k+1)}{2} 个不包含 i 的区间,把他们从总和当中减去即可。
ll n, a[N]; vectorG[N]; cin >> n; for (int i = 1; i <= n; i ++) { cin >> a[i]; G[a[i]].push_back(i); } ll ans = 0; for (int num = 1; num <= n; num ++) { if (G[num].empty()) continue; ll m = G[num].size(), con = calc(n); con -= calc(G[num][0] - 1); for (int i = 1; i < m; i ++) con -= calc(G[num][i] - G[num][i-1] - 1); con -= calc(n - G[num][m-1]); ans += con; } cout << ans << "\n";