个人博客:http://www.kindkidll.com/index.php/archives/156
A-巨木之森
知识点:树的直径
- 求出从每个点出发遍历完
块区域的最短路程,排序后贪心选择最短路程小的点。
- 从一个点出发遍历完
个点并返回起点,要经过每条边两次,现在不返回起点则最短路的终点应该是距离起点最远的点,最短路程等于所有边长度之和的两倍减去起点到其最远点的距离。
- 树上距离一个点最远的点一定是树的直径的端点之一,详见博客树的直径。
- 通过两遍搜索可以得到直径两端点到其余点的距离,即可求出从每个点出发遍历完
块区域的最短路程。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 1e6 + 117;
const int MAXM = 1e6 + 117;
int n;
LL m;
int l, r;// 直径的端点
int head[MAXN], tot;
LL sum, dist[MAXN][2], ans[MAXN];
struct Edge {
int v, ne;
LL w;
} edge[MAXN];
void init() {
l = r = sum = tot = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v, LL w) {
edge[tot].v = v;
edge[tot].w = w;
edge[tot].ne = head[u];
head[u] = tot++;
}
void dfs(int id, int root, int flag) {
for(int i = head[id]; i != -1; i = edge[i].ne) {
if(edge[i].v != root) {
dist[edge[i].v][flag] = dist[id][flag] + edge[i].w;
dfs(edge[i].v, id, flag);
}
}
}
int main() {
scanf("%d%lld", &n, &m);
init();
for(int i = 1; i < n; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addedge(u, v, w);
addedge(v, u, w);
sum += w;
}
dfs(1, 0, 0);
for(int i = 1; i <= n; i++)
if(dist[l][0] < dist[i][0]) l = i;
dist[l][0] = 0;
dfs(l, 0, 0);
for(int i = 1; i <= n; i++)
if(dist[r][0] < dist[i][0]) r = i;
dfs(r, 0, 1);
for(int i = 1; i <= n; i++) ans[i] = sum * 2 - max(dist[i][0], dist[i][1]);
sort(ans + 1, ans + n + 1);
int i;
LL t;
for(i = 1, t = 0; i <= n; i++) {
t += ans[i];
if(t > m) break;
}
printf("%d\n", i - 1);
return 0;
} B-乐***对
知识点:、贪心
将从小到大排序后,设
表示前
个乐手可以组成的乐团数,有状态转移方程:
- 若
,则
。
- 否则,
,优先选择能力值大的组成乐团。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 1e6 + 117;
const int MAXM = 1e6 + 117;
int n;
int a[MAXN], dp[MAXN], num[MAXN];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; i++) {
if(i < a[i]) dp[i] = 0;
else dp[i] = num[i - a[i]] + 1;
num[i] = max(num[i - 1], dp[i]);
}
printf("%d\n", dp[n] == 0 ? -1 : dp[n]);
return 0;
} C-光玉小镇
知识点:状态压缩
- 直接搜索复杂度太高,考虑关键点最多只有15个,对关键点进行建图后两两之间跑
得到最短时间。
- 对关键点建图后直接搜索复杂度还是太高,考虑状态压缩
。设
表示在状态
时最后一个修理
好电线杆所需的最短时间,状态
表示二进制上为1的为对应的电线杆已被修理。
- 状态转移方程详见代码注释部分。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 1e6 + 117;
const int MAXM = 1e6 + 117;
int n, m;
LL t;
LL dp[1 << 16][17], ans;
char ma[207][207];
int sx, sy, tot;
struct pole {
int x, y;
} T[17];
struct node {
int x, y, step;
} now, ne;
bool vis[207][207];
int dx[4] = { -1, 0, 0, 1}, dy[4] = {0, -1, 1, 0};
LL dist1[17], dist2[17][17];
bool check(node a) {
if(a.x < 0 || a.x >= n) return false;
if(a.y < 0 || a.y >= m) return false;
if(ma[a.x][a.y] == '#') return false;
if(vis[a.x][a.y]) return false;
vis[a.x][a.y] = true;
return true;
}
LL bfs(int sx, int sy, int ex, int ey) {
memset(vis, false, sizeof(vis));
queue<node> q;
q.push({sx, sy, 0});
vis[sx][sy] = true;
while(!q.empty()) {
now = q.front();
q.pop();
if(now.x == ex && now.y == ey) return now.step;
for(int i = 0; i < 4; i++) {
ne.x = now.x + dx[i];
ne.y = now.y + dy[i];
ne.step = now.step + 1;
if(check(ne)) q.push(ne);
}
}
return -1;
}
int main() {
scanf("%d%d%lld", &n, &m, &t);
for(int i = 0; i < n; i++) scanf("%s", ma[i]);
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(ma[i][j] == 'S') sx = i, sy = j;
if(ma[i][j] == 'T') {
T[tot].x = i;
T[tot++].y = j;
}
}
}
//关键点建图
memset(dp, INF, sizeof(dp));
for(int i = 0; i < tot; i++) {
dist1[i] = bfs(sx, sy, T[i].x, T[i].y);
dp[1 << i][i] = dist1[i];
if(dist1[i] == -1) {
puts("-1");
return 0;
}
}
for(int i = 0; i < tot; i++) {
for(int j = 0; j < tot; j++) {
dist2[i][j] = bfs(T[i].x, T[i].y, T[j].x, T[j].y);
}
}
//状态i向状态i|j转移
for(int i = 0; i < (1 << tot); i++) {
for(int j = 0; j < tot; j++) {
if(i & (1 << j)) continue;//j已被修理
for(int k = 0; k < tot; k++) {
if((i & (1 << k)) == 0) continue;//k未被修理无法从k转移到j
dp[i | 1 << j][j] = min(dp[i | 1 << j][j], dp[i][k] + dist2[k][j]);
}
}
}
ans = (LL)INF * INF;
for(int i = 0; i < tot; i++) ans = min(ans, dp[(1 << tot) - 1][i] + dist1[i] + tot * t);
printf("%lld\n", ans);
return 0;
} D-巅峰对决
知识点:线段树
题目数据保证任何时候个数字均互不相同,则当区间满足
时区间内的数字是连续的,多次修改和查询使用线段树维护,详见博客线段树从零开始。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 1e6 + 117;
const int MAXM = 1e6 + 117;
int n, q;
int x, op;
struct node {
LL minnum;
LL maxnum;
} tree[MAXN];
void init() {
for(int i = 0; i < MAXN; i++) {
tree[i].minnum = INF;
tree[i].maxnum = -INF;
}
}
void to_up(int i) {
tree[i].minnum = min(tree[i * 2].minnum, tree[i * 2 + 1].minnum);
tree[i].maxnum = max(tree[i * 2].maxnum, tree[i * 2 + 1].maxnum);
}
void update(int i, int l, int r, int p, LL x) {
if(l == r) {
tree[i].minnum = tree[i].maxnum = x;
return;
}
int mid = (l + r) / 2;
if(p <= mid) update(i * 2, l, mid, p, x);
else update(i * 2 + 1, mid + 1, r, p, x);
to_up(i);
}
node require(int i, int l, int r, int left, int right) {
if(left <= l && r <= right) {
return tree[i];
}
node ret, ll, rr;
ret.minnum = INF;
ret.maxnum = -INF;
int mid = (l + r) / 2;
if(left <= mid) {
ll = require(i * 2, l, mid, left, right);
ret.minnum = min(ret.minnum, ll.minnum);
ret.maxnum = max(ret.maxnum, ll.maxnum);
}
if(right > mid) {
rr = require(i * 2 + 1, mid + 1, r, left, right);
ret.minnum = min(ret.minnum, rr.minnum);
ret.maxnum = max(ret.maxnum, rr.maxnum);
}
return ret;
}
int main() {
init();
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++) {
scanf("%d", &x);
update(1, 1, n, i, x);
}
while(q--) {
int l, r;
scanf("%d%d%d", &op, &l, &r);
if(op == 1) update(1, 1, n, l, r);
else {
node now = require(1, 1, n, l, r);
if(now.maxnum - now.minnum == r - l) puts("YES");
else puts("NO");
}
}
return 0;
} E-使徒袭来
知识点:数论
基本不等式:。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 1e6 + 117;
const int MAXM = 1e6 + 117;
int n;
int main() {
scanf("%d", &n);
printf("%.3f\n", 3 * pow(n, 1.0 / 3));
return 0;
} F-核弹剑仙
知识点:搜索
根据武器破坏力的强弱建图,对每个武器搜索一遍比它破坏力大的有多少。
注:出题人题解使用了bitset+拓扑排序,效率更高。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 1e6 + 117;
const int MAXM = 1e6 + 117;
int n, m;
int u, v, cnt;
bool vis[1017];
int a[1017][1017];
void dfs(int i) {
for(int j = 1; j <= n; j++) {
if(a[i][j] && !vis[j]) {
vis[j] = true;
cnt++;
dfs(j);
}
}
}
int main() {
scanf("%d%d", &n, &m);
while(m--) {
scanf("%d%d", &u, &v);
a[v][u] = 1;
}
for(int i = 1; i <= n; i++) {
memset(vis, false, sizeof(vis));
vis[i] = true;
cnt = 0;
dfs(i);
printf("%d\n", cnt);
}
return 0;
} G-虚空之力
知识点:贪心
优先使用第二种方式组成礼炮。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 1e6 + 117;
const int MAXM = 1e6 + 117;
int n;
char s[MAXN * 10];
int cnt[999];
int main() {
scanf("%d%s", &n, s);
for(int i = 0; i < n; i++) cnt[s[i]]++;
int ans = min(cnt['i'], cnt['n']);
ans = min(ans, cnt['g']);
if(ans > cnt['k'] * 2) ans = cnt['k'] * 2;
printf("%d\n", ans);
return 0;
} H-社团游戏
知识点:二维前缀和、二分
二维前缀和预处理出每种小写字母的数量,枚举左上角并二分边长判断是否满足条件。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 1e6 + 117;
const int MAXM = 1e6 + 117;
int n, m, kk;
char s[507][507];
int a[507][507][27];
bool check(int i, int j, int len) {
int x = i + len, y = j + len;
for(int k = 0; k < 27; k++) {
int num = a[x][y][k] - a[i - 1][y][k] - a[x][j - 1][k] + a[i - 1][j - 1][k];
if(num > kk) return false;
}
return true;
}
int main() {
scanf("%d%d%d", &n, &m, &kk);
for(int i = 1; i <= n; i++) scanf("%s", s[i] + 1);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
for(int k = 0; k < 27; k++)
a[i][j][k] = a[i - 1][j][k] + a[i][j - 1][k] - a[i - 1][j - 1][k];
a[i][j][s[i][j] - 'a']++;
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
int l = 0, r = min(n - i, m - j);
while(l <= r) {
int mid = (l + r) / 2;
if(check(i, j, mid)) l = mid + 1;
else r = mid - 1;
}
printf("%d ", r + 1);
}
putchar(10);
}
return 0;
} I-名作之壁
知识点:尺取法、单调队列
- 若区间
的最大值和最小值之差大于
,则区间
的最大值和最小值之差也大于
,对答案的贡献为
。
- 使用单调队列维护区间最大值和最小值。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 1e7 + 117;
const int MAXM = 1e6 + 117;
int n, k;
LL a[MAXN], b, c, ans;
int l1, r1, l2, r2;// l1队头,r1-1队尾
int q1[MAXN], q2[MAXN];// q1最大值,q2最小值
int main() {
scanf("%d%lld", &n, &k);
scanf("%lld%lld%lld", &a[0], &b, &c);
for(int l = 1, r = 1; r <= n; r++) {
a[r] = (a[r - 1] * b + c) % 1000000000;
while(l1 < r1 && a[q1[r1 - 1]] <= a[r]) r1--; //当前值不小于队尾,队尾出队
q1[r1++] = r;
while(l2 < r2 && a[q2[r2 - 1]] >= a[r]) r2--; //当前值不大于队尾,队尾出队
q2[r2++] = r;
while((a[q1[l1]] - a[q2[l2]]) > k) {
ans += n - r + 1;
l++;
while(q1[l1] < l) l1++;//队头不在区间内,队头出队
while(q2[l2] < l) l2++;//队头不在区间内,队头出队
}
}
printf("%lld\n", ans);
return 0;
} J-逃跑路线
知识点:位运算
因为,所以
次操作后的结果只与每次操作的个位有关。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f; ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf; ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 1e6 + 117;
const int MAXM = 1e6 + 117;
int n;
char s[MAXN];
int main() {
scanf("%d", &n);
int sum = 0;
while(n--) {
scanf("%s", s);
int len = strlen(s);
sum += s[len - 1] - '0';
}
printf("%d\n", sum % 2);
return 0;
} 
京公网安备 11010502036488号