A
思路:其实这里用了树的直径的性质:把一棵树砍两半,最远点对一定是这两棵新树里面的最远点对之间的一组组合。
这样预处理
LCA
L
C
A
,然后线段树维护即可。
参考代码:
#include <bits/stdc++.h>
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 10;
struct Edge {
int v, w, nt;
Edge() {}
Edge(int b, int c, int d) : v(b), w(c), nt(d) {}
};
Edge edge[MAXN << 1];
int head[MAXN], edgenum;
int vs[MAXN << 1], depth[MAXN << 1];
int id[MAXN];
int dist[MAXN];
int dfs_clock;
void Init() { edgenum = 0; memset(head, -1, sizeof(head)); }
void addEdge(int u, int v, int w) {
edge[edgenum] = {v, w, head[u]};
head[u] = edgenum++;
}
void DFS(int u, int fa, int d) {
id[u] = dfs_clock;
vs[dfs_clock] = u;
depth[dfs_clock++] = d;
for(int i = head[u]; i != -1; i = edge[i].nt) {
int v = edge[i].v;
if(v == fa) continue;
dist[v] = dist[u] + edge[i].w;
DFS(v, u, d + 1);
vs[dfs_clock] = u;
depth[dfs_clock++] = d;
}
}
void find_depth() {
dfs_clock = 1;
memset(vs, 0, sizeof(vs));
memset(depth, 0, sizeof(depth));
memset(id, 0, sizeof(id));
memset(dist, 0, sizeof(dist));
DFS(1, -1, 0);
}
int dp[MAXN << 1][30];
void RMQ_init(int N) {
for(int i = 1; i <= N; i++) dp[i][0] = i;
for(int j = 1; (1 << j) <= N; j++) {
for(int i = 1; i + (1 << j) - 1 <= N; i++) {
int a = dp[i][j - 1];
int b = dp[i + (1 << (j-1))][j-1];
dp[i][j] = depth[a] < depth[b] ? a : b;
}
}
}
int query(int L, int R) {
int k = 0;
while(1 << (k + 1) <= R - L + 1) k++;
int a = dp[L][k];
int b = dp[R - (1 << k) + 1][k];
return depth[a] < depth[b] ? a : b;
}
int LCA(int u, int v) {
int x = id[u];
int y = id[v];
return x < y ? vs[query(x, y)] : vs[query(y, x)];
}
int Query_Dis(int u, int v) {
return dist[u] + dist[v] - 2 * dist[LCA(u, v)];
}
struct Tree {
int l, r;
pii val;
Tree() {}
Tree(int x, int y, pii z) : l(x), r(y), val(z) {}
};
Tree tree[MAXN << 2];
void gmax(int x, int &y) {
y = max(x, y);
}
pii Work(pii x, pii y) {
int ans1 = Query_Dis(x.first, y.first);
int ans2 = Query_Dis(x.first, y.second);
int ans3 = Query_Dis(x.second, y.first);
int ans4 = Query_Dis(x.second, y.second);
int ans5 = Query_Dis(x.first, x.second);
int ans6 = Query_Dis(y.first, y.second);
int ans = 0;
gmax(ans1, ans);
gmax(ans2, ans);
gmax(ans3, ans);
gmax(ans4, ans);
gmax(ans5, ans);
gmax(ans6, ans);
if(ans == ans1) {
return pii(x.first, y.first);
}
else if(ans == ans2) {
return pii(x.first, y.second);
}
else if(ans == ans3) {
return pii(x.second, y.first);
}
else if(ans == ans4) {
return pii(x.second, y.second);
}
else if(ans == ans5) {
return x;
}
else {
return y;
}
}
void Build(int l, int r, int o) {
tree[o] = {l, r, pii(0, 0)};
if(l == r) {
tree[o].val = pii(l, l);
return ;
}
int mid = l + r >> 1;
Build(l, mid, ll);
Build(mid + 1, r, rr);
tree[o].val = Work(tree[ll].val, tree[rr].val);
}
pii Query(int L, int R, int o) {
if(tree[o].l == L && tree[o].r == R) {
return tree[o].val;
}
int mid = tree[o].l + tree[o].r >> 1;
if(R <= mid) {
return Query(L, R, ll);
}
else if(L > mid) {
return Query(L, R, rr);
}
else {
return Work(Query(L, mid, ll), Query(mid + 1, R, rr));
}
}
int main()
{
int n, q;
while(scanf("%d%d", &n, &q) != EOF) {
Init();
for(int i = 1; i <= n - 1; i++) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
addEdge(u, v, w);
addEdge(v, u, w);
}
find_depth(); RMQ_init(dfs_clock - 1);
Build(1, n, 1);
while(q--) {
int l, r; scanf("%d%d", &l, &r);
pii ans = Query(l, r, 1);
printf("%d\n", Query_Dis(ans.first, ans.second));
}
}
return 0;
}
B
解法一:贪心模拟即可。
解法二:
dp[i][0]
d
p
[
i
]
[
0
]
表示前
i
i
棵且不选第棵的贡献,
dp[i][1]
d
p
[
i
]
[
1
]
表示前
i
i
棵必选第棵的贡献
C
思路:找到不匹配括号的位置
pos
p
o
s
,左(右)括号的贡献是
|str|−pos(pos+1)
|
s
t
r
|
−
p
o
s
(
p
o
s
+
1
)
D
思路:选
1,2,4,8,...,2n
1
,
2
,
4
,
8
,
.
.
.
,
2
n
E
思路:注意到只有
15
15
个点,先预处理每个点的最短路,之后就是状压
dp
d
p
了。
dp[i][j]
d
p
[
i
]
[
j
]
表示状态为
i
i
且最后选择的最优贡献。
F
思路:一个数最多
30
30
次更新就为
0
0
了,直接用线段树维护即可,在每个区间打个标记,看看是否全为
0
0
。
G
思路:找循环节或者按位统计,用快速幂可以很方便的统计。
这里贴个不用快速幂的程序。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 10;
typedef long long LL;
int val[11][4] = {
0, 0, 0, 0,
1, 1, 1, 1,
2, 4, 8, 6,
3, 9, 7, 1,
4, 6, 4, 6,
5, 5, 5, 5,
6, 6, 6, 6,
7, 9, 3, 1,
8, 4, 2, 6,
9, 1, 9, 1,
0, 0, 0, 0
};
int main()
{
int t; scanf("%d", &t);
while(t--) {
int n, k; scanf("%d%d", &n, &k);
int ans = 0;
if(k % 4 == 0) {
k = 3;
}
else {
k = k % 4 - 1;
}
for(int i = 1; i <= 10; i++) {
ans += val[i][k];
ans %= 10;
}
ans *= n / 10; ans %= 10;
n %= 10;
for(int i = 1; i <= n; i++) {
ans += val[i][k];
ans %= 10;
}
printf("%d\n", ans);
}
return 0;
}
H
签到
I
思路:用类似食物链并查集来做,不过你直接也是可行的吧,只是坑点较多。
J
思路:对第四种操作,打个标记即可。其他就很随意了,链表随便搞。