EOJ3335&&hdu6162 Ch’s gift 树剖,dfs序,离线查询,主席树:各显神通
多校第九场的02
北邮出题,hdu上数据极弱
赛后发现数据是个巨型菊花图,所以裸的LCA的大暴力是可以水过的
树剖配合线段树维护最大最小值和区间和也是可以水过的
当然,本文的三种解法不包括水的解法
巨型菊花图???讲道理嘛
章鱼哥加强了本题数据挂在了EOJ3335上
对于正常的数据,这里笔者找到三种解法
dfs序+离线查询
由于没有修改操作,一个显然的想法是离线处理所有问题
将询问拆成1-x,1-y,1-LCA(x,y),则处理的问题转化为从根到节点的链上的问题。
解决这个问题,我们可以在dfs时向treap插入当前的数,在退出时删除这个数,并且每次维护在该点上的答案。
每次ask求a到b的,转化为求1到a-1和1到b,将每次ask的a-1和b同归为k,对所有的k排序去重(ks[]),每次将小于k[i]的所有礼物插入线段树,然后更新有k的ask,然后处理k[i++],实现起来巨烦无比。
dfs序不懂的同学可以看看BZOJ110,本题的简化版,或者,戳这
维护一个长度为2*n的树状数组or线段树
然后分段丢进这个数据结构里面
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 7;
typedef long long LL;
int l[N], r[N];
struct gift{
int pos;
LL val;
void read(int id){
scanf("%lld", &val);
pos = id;
}
bool operator < (const gift & b) const {
return val < b.val;
}
} gifts[N];
int ks[N * 2], K, H;
map<LL, int> hashK;
vector<int> whoAsk[N*2];
void insertK(int id, LL k){
if (hashK.find(k) == hashK.end()) {
hashK[k] = ++H;
whoAsk[H].clear();
}
whoAsk[hashK[k]].push_back(id);
}
struct ask{
int u, v, pos;
LL a, b;
vector<LL> ans;
void read(int pos){
this->pos = pos;
ans.clear();
scanf("%d%d%lld%lld", &u, &v, &a, &b);
a--;
ks[++K] = a, ks[++K] = b;
insertK(pos, a);
insertK(pos, b);
}
inline void print(){
printf("%lld", abs(ans[1] - ans[0]));
}
} asks[N];
struct binaryIndexTree{
LL val[N * 2];
int n;
inline void build(int n){
this->n = n;
memset(val, 0, sizeof(val));
}
inline void add(int k, LL num){
for (;k <= n; k += k&-k) val[k] += num;
}
LL sum(int k){
if (k == 0) return 0;
LL sum = 0;
for (; k; k -= k&-k) sum += val[k];
return sum;
}
} TT ;
struct segTree{
LL tree[N * 6];
int M;
inline void build(int n){
M = 1; for(;M<n;) M<<=1; if(M!=1)M--;
memset(tree, sizeof(tree), 0);
}
void add(int t, LL x){
for (tree[t+=M]+=x, t>>=1; t; t>>=1){
tree[t] = tree[t<<1] + tree[t<<1^1];
}
}
LL sum(int l, int r){
if (l > r || r == 0) return 0;
LL ans = 0;
for (l+=M-1,r+=M+1; l^r^1; l>>=1,r>>=1){
if (~l&1) ans += tree[l^1];
if ( r&1) ans += tree[r^1];
}
return ans;
}
} T;
struct graph{
struct Edge{
int from, to, nxt;
Edge(){}
Edge(int u, int v, int n):from(u), to(v), nxt(n){}
} edges[N * 2];
static const int LCADEP = 17;
int n, E, head[N];
int top, dep[N], fa[N][LCADEP + 1];
inline void AddEdge(int f, int t){
edges[++E] = Edge(f, t, head[f]);
head[f] = E;
}
inline void Init(int n){
this -> n = n ; E = -1; top = 0; dep[0] = 0;
for (int i = 0; i <= n; i++) head[i] = -1;
memset(fa, 0, sizeof(fa));
}
void dfs(int u, int pre){
l[u] = ++top;
//printf("l[%d] = %d\n", u, top);
fa[u][0] = pre;
dep[u] = dep[pre] + 1;
for (int i = 1; i <= LCADEP; i++){
if (dep[u] < (1<<i)) break;
fa[u][i] = fa[fa[u][i-1]][i-1];
}
for (int i = head[u]; i != -1; i = edges[i].nxt){
if (edges[i].to != pre) dfs(edges[i].to, u);
}
r[u] = ++top;
//printf("r[%d] = %d\n", u, top);
}
int lca(int x, int y){
if (dep[x] < dep[y]) swap(x,y);
int t = dep[x] - dep[y];
for (int i = 0; i <= LCADEP; i++) if ((1<<i) & t) x = fa[x][i];
for (int i = LCADEP; i >= 0; i--) if (fa[x][i] != fa[y][i]){
x = fa[x][i]; y = fa[y][i];
}
return x==y ? x : fa[x][0];
}
void solve(ask &a){
int u = a.u, v = a.v;
int f = lca(u, v);
LL ans = T.sum(1, l[u]) + T.sum(1, l[v]) - T.sum(1, l[f]) - T.sum(1, l[fa[f][0]]);
//LL ans = T.sum(l[u]) + T.sum(l[v]) - T.sum(l[f]) - T.sum(l[fa[f][0]]);
a.ans.push_back(ans);
}
} g ;
int main () {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int n, m, u, v;
for (; cin >> n >> m;) {
for(int i = 1; i <= n; i++) gifts[i].read(i);
sort(gifts + 1, gifts + n+1);
g.Init(n);
for(int i = 0; i < n - 1; i++) {
scanf("%d%d", &u, &v);
g.AddEdge(u, v);
g.AddEdge(v, u);
}
g.dfs(1, 0);
T.build(n*2);
K = 0, H = 0;
hashK.clear();
for (int i = 1; i <= m; i++) asks[i].read(i);
sort(ks + 1, ks + K+1);
K = unique(ks + 1, ks + K+1) - (ks + 1);
int cur = 1;
for (int i = 1; i <= K; i++){
//printf("ks[%d] = %d\n", i, ks[i]);
for (int &j = cur; j <= n; j++){
if (gifts[j].val > ks[i]) break;
//printf("gifts[%d].val = %d, pos = %d, [%d, %d]\n", j, gifts[j].val, gifts[j].pos, l[gifts[j].pos], r[gifts[j].pos]);
T.add(l[gifts[j].pos], gifts[j].val);
T.add(r[gifts[j].pos],-gifts[j].val);
}
int kk = hashK[ks[i]];
for (int j = 0; j < whoAsk[kk].size(); j++){
ask &a = asks[whoAsk[kk][j]];
g.solve(a);
}
}
for (int i = 1; i <= m; i++){
asks[i].print();
putchar(i==m ? '\n' : ' ');
}
}
return 0;
}c++
树链剖分+离线查询
这样维护的数据结构大小为n,而且不用写lca
当然也可以将所有的查询和点权排序,用树链剖分做这个题,在线段树上面插入就ok。
树剖其实是最直接最粗暴的将树映射到了线段树上,没有了dfs序的加法减法还有lca,各种前缀和
直接寻找路径
#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 3e5 + 7;
typedef long long LL;
int n;
struct gift{
int pos, val;
void read(int id){
scanf("%d", &val);
pos = id;
}
bool operator < (const gift & b) const {
return val < b.val;
}
} gifts[N];
struct segmentTree{
#define lc (t<<1)
#define rc (t<<1^1)
LL sum[N];
int M;
inline void build(int n){
M = 1; for(;M<n;)M<<=1; if(M!=1)M--;
memset(sum, sizeof(sum), 0);
}
void add(int t, LL x){
for (sum[t+=M]+=x, t>>=1; t; t>>=1){
sum[t] = sum[lc] + sum[rc];
}
}
LL query(int l, int r){
LL ans = 0;
for (l+=M-1,r+=M+1; l^r^1; l>>=1,r>>=1){
if (~l&1) ans += sum[l^1];
if ( r&1) ans += sum[r^1];
}
return ans;
}
} T;
struct TreeChain{
struct Edge{
int from, to, nxt;
Edge(){}
Edge(int u, int v, int n):
from(u), to(v), nxt(n){}
}edges[N];
int n, E, head[N];
int tim;
int siz[N]; //用来保存以x为根的子树节点个数
int top[N]; //用来保存当前节点的所在链的顶端节点
int son[N]; //用来保存重儿子
int dep[N]; //用来保存当前节点的深度
int fa[N]; //用来保存当前节点的父亲
int tid[N]; //用来保存树中每个节点剖分后的新编号,线段树
int Rank[N];//tid反向数组,不一定需要
inline void AddEdge(int f, int t){
edges[++E] = Edge(f, t, head[f]);
head[f] = E;
}
inline void Init(int n){
tim = 0;
this -> n = n ; E = -1;
for (int i = 0; i <= n; i++) head[i] = -1;
for (int i = 0; i <= n; i++) son[i] = -1;
}
void dfs1(int u, int father, int d){
dep[u] = d;
fa[u] = father;
siz[u] = 1;
int nxt;
for(int i = head[u]; i != -1; i = nxt){
Edge &e = edges[i]; nxt = e.nxt;
if (e.to == father) continue;
dfs1(e.to, u, d + 1);
siz[u] += siz[e.to];
if(son[u]==-1 || siz[e.to] > siz[son[u]]) son[u] = e.to;
}
}
void dfs2(int u, int tp){
top[u] = tp;
tid[u] = ++tim;
Rank[tid[u]] = u;
if (son[u] == -1) return;
dfs2(son[u], tp);
int nxt;
for(int i = head[u]; i != -1; i = nxt){
Edge &e = edges[i]; nxt = e.nxt;
if(e.to == son[u] || e.to == fa[u]) continue;
dfs2(e.to, e.to);
}
}
LL query(int u, int v){
int f1 = top[u], f2 = top[v];
LL tmp = 0;
for (; f1 != f2;){
if (dep[f1] < dep[f2]){
swap(f1, f2);
swap(u, v);
}
tmp += T.query(tid[f1], tid[u]);
u = fa[f1]; f1 = top[u];
}
if (dep[u] > dep[v]) swap(u, v);
return tmp + T.query(tid[u], tid[v]);
}
} g ;
int ks[N], K, H;
map<int, int> hashK;
vector<int> whoAsk[N*2];
void insertK(int id, int k){
if (hashK.find(k) == hashK.end()) {
hashK[k] = ++H;
whoAsk[H].clear();
}
whoAsk[hashK[k]].push_back(id);
}
struct ask{
int u, v, a, b, pos;
vector<LL> ans;
void read(int pos){
this->pos = pos;
ans.clear();
scanf("%d%d%d%d", &u, &v, &a, &b);
a--;
ks[++K] = a, ks[++K] = b;
insertK(pos, a);
insertK(pos, b);
}
} asks[N];
int main () {
freopen("in.txt", "r", stdin);
int m, u, v;
while(cin >> n >> m) {
for(int i = 1; i <= n; i++) {
gifts[i].read(i);
}
sort(gifts + 1, gifts + n+1);
g.Init(n);
for(int i = 0; i < n - 1; i++) {
scanf("%d%d", &u, &v);
g.AddEdge(u, v);
g.AddEdge(v, u);
}
g.dfs1(1, -1, 0);
g.dfs2(1, 1);
T.build(n);
K = 0, H = 0;
hashK.clear();
for (int i = 1; i <= m; i++) asks[i].read(i);
sort(ks + 1, ks + K+1);
K = unique(ks + 1, ks + K+1) - (ks + 1);
int cur = 1;
for (int i = 1; i <= K; i++){
for (int &j = cur; j <= n; j++){
if (gifts[j].val > ks[i]) break;
T.add(g.tid[gifts[j].pos], gifts[j].val);
}
int kk = hashK[ks[i]];
for (int j = 0; j < whoAsk[kk].size(); j++){
ask &a = asks[whoAsk[kk][j]];
a.ans.push_back(g.query(a.u, a.v));
}
}
for (int i = 1; i <= m; i++){
printf("%lld", abs(asks[i].ans[1] - asks[i].ans[0]));
putchar(i==m ? '\n' : ' ');
}
}
return 0;
}
直接套个主席树
话说,离线查询那么难写,都是不会主席树的下策,主席树会了谁管那么多
主席树刚刚开坑,上篇blog写了一些,这份代码在hdu上可以通过
还没有通过章鱼哥的加强数据,,待续
29日中午更新:
通过了章鱼哥的数据,因为
一开始写了n,导致离散化血崩
#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 7;
typedef long long LL;
LL gift[N], Rank[N];//节点权值和离散化
struct ChairTree{
#define sum(x) tree[x].sum
#define lson tree[rt].lc, tree[rt1].lc, l, m
#define rson tree[rt].rc, tree[rt1].rc, m+1, r
struct node{
int lc, rc;
LL sum;
} tree[N * 30];
int n, root[N], cnt;
inline void build(int _n){
n = _n; cnt = 0;
}
void add(int pos, LL val, int &rt, int rt1, int l, int r){
tree[rt = ++cnt] = tree[rt1];
tree[rt].sum += val;
if (l == r) return;
int m = (l + r) >> 1;
if (pos <= m) add(pos, val, lson);
else add(pos, val, rson);
}
LL query(int L, int R, int rt, int rt1, int l, int r){
if (L <= l && r <= R) return sum(rt1) - sum(rt);
if (sum(rt1) == 0) return 0;
if (sum(rt1) == sum(rt)) return 0;
LL ans = 0;
int m = (l + r) >> 1;
if (L <= m) ans += query(L, R, lson);
if (m < R) ans += query(L, R, rson);
return ans;
}
#undef sum(x)
#undef lson
#undef rson
} T;
struct graph{
struct Edge{
int from, to, nxt;
Edge(){}
Edge(int u, int v, int n):from(u), to(v), nxt(n){}
} edges[N * 2];
static const int LCADEP = 17;
int n, E, head[N];
int top, dep[N], fa[N][LCADEP + 1];
inline void AddEdge(int f, int t){
edges[++E] = Edge(f, t, head[f]);
head[f] = E;
}
inline void Init(int n){
this -> n = n ; E = -1; top = 0; dep[0] = 0;
for (int i = 0; i <= n; i++) head[i] = -1;
memset(fa, 0, sizeof(fa));
}
void dfs(int u, int pre){
T.add(gift[u], Rank[gift[u]], T.root[u], T.root[pre], 1, T.n);
fa[u][0] = pre;
dep[u] = dep[pre] + 1;
for (int i = 1; i <= LCADEP; i++){
if (dep[u] < (1<<i)) break;
fa[u][i] = fa[fa[u][i-1]][i-1];
}
for (int i = head[u]; i != -1; i = edges[i].nxt){
if (edges[i].to != pre) dfs(edges[i].to, u);
}
}
int lca(int x, int y){
if (dep[x] < dep[y]) swap(x,y);
int t = dep[x] - dep[y];
for (int i = 0; i <= LCADEP; i++) if ((1<<i) & t) x = fa[x][i];
for (int i = LCADEP; i >= 0; i--) if (fa[x][i] != fa[y][i]){
x = fa[x][i]; y = fa[y][i];
}
return x==y ? x : fa[x][0];
}
LL query(int u, int v, int L, int R){
int f = lca(u, v);
LL ans = 0;
ans += T.query(L, R, T.root[f], T.root[u], 1, T.n);
ans += T.query(L, R, T.root[fa[f][0]], T.root[v], 1, T.n);
return ans;
}
} g ;
int main () {
//freopen("in.txt", "r", stdin);
int n, q, u, v;
for (LL a, b; ~scanf("%d%d", &n, &q);) {
for(int i = 1; i <= n; i++) {
scanf("%lld", &gift[i]);
Rank[i] = gift[i];
}
sort(Rank + 1, Rank + n+1);
int un = unique(Rank + 1, Rank + n+1) - (Rank+1);
for (int i = 1; i <= n; i++){
gift[i] = lower_bound(Rank + 1, Rank + un+1, gift[i]) - Rank;
}
g.Init(n);
for(int i = 0; i < n - 1; i++) {
scanf("%d%d", &u, &v);
g.AddEdge(u, v);
g.AddEdge(v, u);
}
T.build(un);
g.dfs(1, 0);
for (; q--;){
scanf("%d%d%lld%lld", &u, &v, &a, &b);
int aa = lower_bound(Rank+1, Rank + un+1, a) - Rank;
if (Rank[aa] < a) aa++;
int bb = lower_bound(Rank+1, Rank + un+1, b) - Rank;
if (bb > un || Rank[bb] > b) bb--;
printf("%lld", g.query(u, v, aa, bb));
putchar(q==0 ? '\n' : ' ');
}
}
return 0;
}