题目链接:https://nanti.jisuanke.com/t/38229
题目大意:
给一颗树,m次查询ui->vi这条链中边权小于等于ki的边数。
做法一:树链剖分+主席树(这里并不需要进行离散化,但是比赛的时候忘了)
#include <bits/stdc++.h>
#define mid (l+r>>1)
#define lson (o<<1)
#define rson (o<<1|1)
#define all(x) (x).begin(),(x).end()
using namespace std;
const int N = 2e5+1000;
vector<int>nxt[N],len[N];
int fa[N],deep[N],id[N],siz[N],son[N],tot,s[N],top[N],ss[N];
int n,m;
int SIZ;
struct TREE{
int l,r,x;
}tree[N*20];
int rk[N],Tot,rt[N];
void insert(int &o,int l,int r,int data){
tree[++Tot] = tree[o];
o = Tot;
tree[o].x++;
if(l==r) return ;
if(mid>=data) insert(tree[o].l,l,mid,data);
else insert(tree[o].r,mid+1,r,data);
}
int query(int lo,int ro,int l,int r,int h,int t){
if(l>=h&&r<=t) return tree[ro].x-tree[lo].x;
int ans = 0;
if(mid>=h) ans += query(tree[lo].l,tree[ro].l,l,mid,h,t);
if(mid<t) ans += query(tree[lo].r,tree[ro].r,mid+1,r,h,t);
return ans;
}
void dfs(int u,int f) {
deep[u] = deep[f]+1;
son[u] = 0;
siz[u] = 1;
fa[u] = f;
for(int i = 0; i < nxt[u].size(); i++) {
int v = nxt[u][i];
int w = len[u][i];
if(v==f) continue;
s[v] = w;
dfs(v,u);
siz[u] += siz[v];
if(siz[son[u]]<siz[v]) son[u] = v;
}
}
void dfs2(int u,int t) {
id[u] = ++tot;
ss[id[u]] = s[u];
top[u] = t;
if(son[u]) dfs2(son[u],t);
for(auto v:nxt[u]) {
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int Query(int u,int v,int x) {
int tu = top[u];
int tv = top[v];
int ans = 0;
while(tu!=tv) {
if(deep[tu]<deep[tv]) {
swap(u,v);
swap(tu,tv);
}
ans += query(rt[id[tu]-1],rt[id[u]],1,SIZ,1,x);
u = fa[tu];
tu = top[u];
}
if(u==v) return ans;
if(id[u]>id[v]) swap(u,v);
ans += query(rt[id[u]],rt[id[v]],1,SIZ,1,x);
return ans;
}
struct Q{
int u, v, w;
}Q[N];
vector<int> k;
int main() {
scanf("%d%d",&n,&m);
for(int i = 1; i < n; i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
nxt[u].push_back(v);
nxt[v].push_back(u);
len[u].push_back(w);
len[v].push_back(w);
k.push_back(w);
}
for(int i = 1; i <= m; i++) {
int u, v, x;
scanf("%d%d%d",&Q[i].u,&Q[i].v,&Q[i].w);
k.push_back(Q[i].w);
}
sort(all(k));
k.erase(unique(all(k)),k.end());
SIZ = k.size()+10;
dfs(1,0);
dfs2(1,1);
rt[0] = 0;
tree[0].l = tree[0].r = tree[0].x = 0;
for(int i = 1; i <= n; i++) {
rt[i] = rt[i-1];
int x = lower_bound(all(k),ss[i])-k.begin()+1;
insert(rt[i],1,SIZ,x);
}
for(int i = 1; i <= m; i++) {
int x = lower_bound(all(k),Q[i].w)-k.begin()+1;
printf("%d\n",Query(Q[i].u,Q[i].v,x));
}
return 0;
}
-----------------------------------------------------------------更新线--------------------2019.8.3----------------------------------------------
因为看到评论有人问离散化,所以在这里稍微解释一下为什么这题不需要离散化。
首先我们要清楚主席树的空间复杂度,在一般情况下主席树是一颗可持久化的权值线段树,
假如给一个长度为N的数组,数组元素的大小在[1,MAX]之间,你需要查询 [L,R] 内小于等于K的元素有多少个。
显然主席树会执行N次insert操作,最坏情况下每一次insert操作会开辟一条新的链。
我们考虑这条链的长度,如果我们不离散化的话,主席树的根节点所维护的左右端点就是[1,MAX],这条链延伸log(MAX)的长度就到了叶子节点,因为每向下一层,所维护的区间长度就会减少一半。
主席树的空间复杂度为O(N*log(MAX)), 单次查询的时间复杂度为O(log(MAX))
假如我们进行了离散化,那么离散化后的数值大小将在[1,N]以内,即离散化后MAX = N。
依然按上面的步骤分析复杂度,得主席树的空间复杂度为O(N*log(N)), 单次查询的时间复杂度为O(log(N))
而在本题中log(N) = log(1e5) <= 20 , log(MAX) = log(1e9) <= 32 , 因为都是log级别所以两者相差不大,所以我们并不需要离散化。
下面附上没有离散化AC的代码
#include <bits/stdc++.h>
#define mid (l+r>>1)
#define lson (o<<1)
#define rson (o<<1|1)
#define all(x) (x).begin(),(x).end()
using namespace std;
const int N = 2e5+1000;
vector<int>nxt[N],len[N];
int fa[N],deep[N],id[N],siz[N],son[N],tot,s[N],top[N],ss[N];
int n,m;
int SIZ;
struct TREE{
int l,r,x;
}tree[N*40];
int rk[N],Tot,rt[N];
void insert(int &o,int l,int r,int data){
tree[++Tot] = tree[o];
o = Tot;
tree[o].x++;
if(l==r) return ;
if(mid>=data) insert(tree[o].l,l,mid,data);
else insert(tree[o].r,mid+1,r,data);
}
int query(int lo,int ro,int l,int r,int h,int t){
if(l>=h&&r<=t) return tree[ro].x-tree[lo].x;
int ans = 0;
if(mid>=h) ans += query(tree[lo].l,tree[ro].l,l,mid,h,t);
if(mid<t) ans += query(tree[lo].r,tree[ro].r,mid+1,r,h,t);
return ans;
}
void dfs(int u,int f) {
deep[u] = deep[f]+1;
son[u] = 0;
siz[u] = 1;
fa[u] = f;
for(int i = 0; i < nxt[u].size(); i++) {
int v = nxt[u][i];
int w = len[u][i];
if(v==f) continue;
s[v] = w;
dfs(v,u);
siz[u] += siz[v];
if(siz[son[u]]<siz[v]) son[u] = v;
}
}
void dfs2(int u,int t) {
id[u] = ++tot;
ss[id[u]] = s[u];
top[u] = t;
if(son[u]) dfs2(son[u],t);
for(auto v:nxt[u]) {
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int Query(int u,int v,int x) {
int tu = top[u];
int tv = top[v];
int ans = 0;
while(tu!=tv) {
if(deep[tu]<deep[tv]) {
swap(u,v);
swap(tu,tv);
}
ans += query(rt[id[tu]-1],rt[id[u]],1,SIZ,1,x);
u = fa[tu];
tu = top[u];
}
if(u==v) return ans;
if(id[u]>id[v]) swap(u,v);
ans += query(rt[id[u]],rt[id[v]],1,SIZ,1,x);
return ans;
}
struct Q{
int u, v, w;
}Q[N];
vector<int> k;
int main() {
scanf("%d%d",&n,&m);
for(int i = 1; i < n; i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w); //这里我让权值全部加了1,因为我的旧代码里的左端点是从1开始的,就不再修改了。
w++;
nxt[u].push_back(v);
nxt[v].push_back(u);
len[u].push_back(w);
len[v].push_back(w);
}
for(int i = 1; i <= m; i++) {
int u, v, x;
scanf("%d%d%d",&Q[i].u,&Q[i].v,&Q[i].w);
Q[i].w++;
}
SIZ = 1e9+10; //右端点直接设为最大值
dfs(1,0);
dfs2(1,1);
rt[0] = 0;
tree[0].l = tree[0].r = tree[0].x = 0;
for(int i = 1; i <= n; i++) {
rt[i] = rt[i-1];
insert(rt[i],1,SIZ,ss[i]);
}
for(int i = 1; i <= m; i++)
printf("%d\n",Query(Q[i].u,Q[i].v,Q[i].w));
return 0;
}
--------------------------------------更新结束线---------------------------------2019.8.3------------------------------------------
做法二:按照dfs顺序在树上直接建主席树
#include <bits/stdc++.h>
#define mid (l+r>>1)
#define lson (o<<1)
#define rson (o<<1|1)
#define all(x) (x).begin(),(x).end()
using namespace std;
const int N = 2e5+1000;
vector<int>nxt[N],len[N],k;
int fa[N][20],deep[N],tot;
int n,m;
int SIZ=1e9;
struct TREE{
int l,r,x;
}tree[N*20];
int rk[N],Tot,rt[N];
void insert(int &o,int l,int r,int data){
tree[++Tot] = tree[o];
o = Tot;
tree[o].x++;
if(l==r) return ;
if(mid>=data) insert(tree[o].l,l,mid,data);
else insert(tree[o].r,mid+1,r,data);
}
int query(int lo,int ro,int l,int r,int h,int t){
if(l>=h&&r<=t) return tree[ro].x-tree[lo].x;
int ans = 0;
if(mid>=h) ans += query(tree[lo].l,tree[ro].l,l,mid,h,t);
if(mid<t) ans += query(tree[lo].r,tree[ro].r,mid+1,r,h,t);
return ans;
}
void dfs(int u, int f, int d) {
deep[u] = d;
fa[u][0] = f;
int siz = nxt[u].size();
for(int i = 0; i < siz; i++) {
int v = nxt[u][i];
int w = len[u][i];
if(v == f) continue;
rt[v] = rt[u];
insert(rt[v],0,SIZ,w);
dfs(v, u, d+1);
}
}
void init(int n) {
for(int j = 1; j <= 19; j++)
for(int i = 1; i <= n; i++)
fa[i][j] = fa[fa[i][j-1]][j-1];
}
int lca(int u, int v){
if(deep[u] < deep[v]) swap(u, v);
int diff = deep[u] - deep[v];
for(int i = 19; i >= 0; i--)
if(diff>>i&1)
u = fa[u][i];
if(u == v) return u;
for(int i = 19; i >= 0; i--)
if(fa[u][i] != fa[v][i]) {
u = fa[u][i];
v = fa[v][i];
}
return fa[u][0];
}
int Query(int u,int v,int x) {
int uv = lca(u,v);
return query(rt[uv],rt[u],0,SIZ,0,x) + query(rt[uv],rt[v],0,SIZ,0,x);
}
struct Q{
int u, v, w;
}Q[N];
int main() {
scanf("%d%d",&n,&m);
for(int i = 1; i < n; i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
nxt[u].push_back(v);
nxt[v].push_back(u);
len[u].push_back(w);
len[v].push_back(w);
}
rt[0] = 0;
tree[0].l = tree[0].r = tree[0].x = 0;
dfs(1,0,1);
init(n);
for(int i = 1; i <= m; i++) {
int u, v, x;
scanf("%d%d%d",&u,&v,&x);
printf("%d\n",Query(u,v,x));
}
return 0;
}
做法三:离线,树链剖分,排序加边,树状数组维护。