A-Rainyrabbit 爱邮递
算法 1
每次重建快递站后直接深搜一遍累加贡献就好了,然后 O ( 1 ) \mathcal{O}(1) O(1) 回答。
你可以获得 10pts 的好成绩。
算法 2
菊花图,这个很简单啊。
如果加的是花心,相当于其他城市都加 1 1 1,否则就是花心的城市加 1 1 1,其他城市加 2 2 2。
结合算法 1 可以获得 30pts 的好成绩。
算法 3
一条链的话也是送的,直接拆拆贡献用棵线段树维护即可。
结合算法 1,2 可以获得 60pts 的好成绩。
算法 4
做法 1
考虑树怎么做,很套路的可以将 d i s ( x , y ) dis(x,y) dis(x,y) 拆成 d e p x + d e p y − 2 d e p lca ( x , y ) dep_x+dep_y-2dep_{\operatorname{lca}(x,y)} depx+depy−2deplca(x,y),但是拆了还是不好做,修改最坏还是 O ( n ) \mathcal{O(n)} O(n) 的。
发现 d e p lca ( x , y ) dep_{\operatorname{lca}(x,y)} deplca(x,y) 这个玩意可以借助 [LNOI2014]LCA 的思想,将树根 1 1 1 到 x x x 上的边都加上一次这条边的权值,然后节点 y y y 往上爬,途中边排边累加该边的贡献,爬到根后就是 d e p lca ( x , y ) dep_{\operatorname{lca}(x,y)} deplca(x,y) 的值。
于是便有了一个新的算法,对于重建快递站到 u u u,直接将 u u u 往上爬,并更新每条边新的贡献,查询的话就跟刚才一样的往上爬就好了。
暴力往上爬的话可以过数据随机的点,然后结合算法 1,2,3 就能获得 80pts 的好成绩。
正解直接用树链剖分优化即可。
时间复杂度 O ( n log 2 n ) \mathcal{O}(n\log^2 n) O(nlog2n)。
#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
#define int long long
int n, m, totdep, tot, a[200005], Dep[200005], fa[200005], size[200005], dep[200005], son[200005],
seg[200005], rev[200005], top[200005];
struct node {
int to, w;
};
vector<node> G[200005];
void dfs1(int u, int f) {
size[u] = 1, dep[u] = dep[f] + 1, fa[u] = f;
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i].to;
if (v == f)
continue;
Dep[v] = Dep[u] + G[u][i].w;
a[v] = G[u][i].w;
dfs1(v, u);
size[u] += size[v];
if (size[v] > size[son[u]])
son[u] = v;
}
}
void dfs2(int u, int f) {
if (son[u]) {
seg[son[u]] = ++seg[0];
top[son[u]] = top[u];
rev[seg[0]] = son[u];
dfs2(son[u], u);
}
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i].to;
if (v == f)
continue;
if (!top[v]) {
seg[v] = ++seg[0];
top[v] = v;
rev[seg[0]] = v;
dfs2(v, u);
}
}
}
struct Segment_Tree {
struct node {
LL xsum, sum, lazy;
};
node tree[800005];
#define lson(x) x << 1
#define rson(x) x << 1 | 1
void build(int x, int l, int r) {
if (l == r) {
tree[x].sum = a[rev[l]];
return;
}
int mid = (l + r) >> 1;
build(lson(x), l, mid);
build(rson(x), mid + 1, r);
tree[x].sum = tree[lson(x)].sum + tree[rson(x)].sum;
tree[x].xsum = tree[lson(x)].xsum + tree[rson(x)].xsum;
}
void push_down(int x) {
if (tree[x].lazy) {
tree[lson(x)].lazy += tree[x].lazy;
tree[rson(x)].lazy += tree[x].lazy;
tree[lson(x)].xsum += tree[x].lazy * tree[lson(x)].sum;
tree[rson(x)].xsum += tree[x].lazy * tree[rson(x)].sum;
tree[x].lazy = 0;
}
}
void add(int x, int l, int r, int L, int R) {
if (L <= l && r <= R) {
tree[x].lazy++;
tree[x].xsum += tree[x].sum;
return;
}
push_down(x);
int mid = (l + r) >> 1;
if (L <= mid)
add(lson(x), l, mid, L, R);
if (mid + 1 <= R)
add(rson(x), mid + 1, r, L, R);
tree[x].xsum = tree[lson(x)].xsum + tree[rson(x)].xsum;
}
LL query(int x, int l, int r, int L, int R) {
if (L <= l && r <= R)
return tree[x].xsum;
LL ans = 0;
int mid = (l + r) >> 1;
push_down(x);
if (L <= mid)
ans += query(lson(x), l, mid, L, R);
if (mid + 1 <= R)
ans += query(rson(x), mid + 1, r, L, R);
return ans;
}
} T;
void change(int u) {
while (u) {
int fu = top[u];
T.add(1, 1, n, seg[fu], seg[u]);
u = fa[fu];
}
}
LL query(int u) {
LL ans = 0;
while (u) {
int fu = top[u];
ans += T.query(1, 1, n, seg[fu], seg[u]);
u = fa[fu];
}
return ans;
}
signed main() {
scanf("%lld %lld", &n, &m);
for (int i = 1; i < n; i++) {
int u, v, w;
scanf("%lld %lld %lld", &u, &v, &w);
G[u].push_back(node{
v, w });
G[v].push_back(node{
u, w });
}
dfs1(1, 0);
seg[0] = 1, seg[1] = 1, top[1] = rev[1] = 1;
dfs2(1, 0);
T.build(1, 1, seg[0]);
while (m--) {
int opt, u;
scanf("%lld %lld", &opt, &u);
if (opt == 1)
change(u), totdep += Dep[u], tot++;
else
printf("%lld\n", totdep + 1ll * tot * Dep[u] - 2 * query(u));
}
return 0;
}
做法 2
来自巨佬 WY 的做法,cdq 分治+虚树。
B-Rainyrabbit 爱回文
很遗憾,因为数据过水,导致有 5 人 AC,但是本质上只有 3 人写的正解,其他的时间复杂度都是错的。
多次询问一个字符串 s s s,问构造若干字符串 t 1 , t 2 , ⋯ , t k t_1,t_2,\cdots ,t_k t1,t2,⋯,tk 使得 s = t 1 t 2 ⋯ t k s=t_1t_2\cdots t_k s=t1t2⋯tk 并且 ∀ i ∈ [ 1 , k ] , t i ≥ 2 \forall i \in [1,k],t_i \geq 2 ∀i∈[1,k],ti≥2 并且是回文串的方式是否存在。 T ≤ 10 , ∣ s ∣ ≤ 1 0 6 T \leq 10,|s| \leq 10^6 T≤10,∣s∣≤