【NOI模拟赛】经典老树题(启发式合并,扫描线)

本文介绍了一种高效的查询处理方法,通过启发式合并set和LCA筛选,将复杂度降低到O(n log² n),主要讨论了如何利用子树集合合并、LCA相同点对筛选及扫描线技术来减少查询时间,适用于大规模数据结构问题的优化。
摘要由CSDN通过智能技术生成

题面

在这里插入图片描述
2s,512mb

题解

如果有两对点 ( a , b ) , ( c , d ) (a,b),(c,d) (a,b),(c,d) LCA相同,且 c ≤ a ≤ b ≤ d c\leq a\leq b\leq d cabd ,那么我们只需要保留 ( a , b ) (a,b) (a,b) 就够了。

我们用启发式合并 set 维护每个点的子树集合,合并的时候就可以处理出初步筛选的有效点对(小集合的每个点在大集合中找前驱后继),这时有效点对的总个数是 O ( n log ⁡ n ) O(n\log n) O(nlogn) 的。

我们把 d e p ( l c a ( x , y ) ) dep(lca(x,y)) dep(lca(x,y)) 相等的所有点对 ( x , y ) (x,y) (x,y) 再进行筛选,得到互不包含的点对,此时就可以把询问离线下来,用扫描线添加点对。对于两个点对 ( a , c ) , ( b , d ) (a,c),(b,d) (a,c),(b,d) a ≤ b ≤ c ≤ d a\leq b\leq c\leq d abcd ,那么 ( b , d ) (b,d) (b,d) 就对所有 a < l ≤ b , d ≤ r a<l\leq b,d\leq r a<lb,dr 的询问产生 1 的贡献、

时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)

CODE

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#pragma GCC optimize(2)
using namespace std;
#define MAXN 100005
#define LL long long
#define ULL unsigned long long
#define UIN unsigned int
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define PR pair<int,int>
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
//#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

const int MOD = 998244353;
int n,m,s,o,k;
int hd[MAXN],nx[MAXN<<1],v[MAXN<<1],w[MAXN<<1],cne;
void ins(int x,int y,int z) {
    nx[++ cne] = hd[x]; v[cne] = y; hd[x] = cne; w[cne] = z;
}
LL d[MAXN];
set<int> st[MAXN];
map<LL,set<PR>> mp;
void merg(set<int>&a,set<int>&b,LL de) {
    if(a.size() < b.size()) swap(a,b);
    for(auto i = b.begin();i != b.end();i ++) {
        auto j = a.lower_bound(*i);
        if(j != a.end()) mp[de].insert({-(*i),*j});
        if(j != a.begin()) mp[de].insert({-(*(--j)),*i});
    }
    for(auto i = b.begin();i != b.end();i ++) a.insert(*i);
    b.clear();
}
void dfs(int x,int ff) {
    st[x].insert(x); mp[d[x]].insert({-x,x});
    for(int i = hd[x];i;i = nx[i]) {
        if(v[i] != ff) {
            d[v[i]] = d[x] + w[i];
            dfs(v[i],x);
            merg(st[x],st[v[i]],d[x]);
        }
    } return ;
}
vector<PR> bu[MAXN];
int qr[MAXN*5],as[MAXN*5];
vector<int> qu[MAXN];
int c[MAXN];
void addc(int x,int y) {while(x<=n)c[x]+=y,x+=lowbit(x);}
int sum(int x) {int s=0;while(x>0)s+=c[x],x&=(x-1);return s;}
int main() {
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
    n = read(); m = read();
    for(int i = 1;i < n;i ++) {
        s = read();o = read();k = read();
        ins(s,o,k); ins(o,s,k);
    }
    dfs(1,0);
    for(auto i = mp.begin();i != mp.end();i ++) {
        auto &se = i->SE;
        int mn = 0x3f3f3f3f;
        for(auto j = se.begin();j != se.end();j ++) {
            if(j->SE < mn) {
                bu[-(j->FI)].push_back({j->SE,mn-1});
                mn = j->SE;
            }
        }
    }
    for(int i = 1;i <= m;i ++) {
        s = read(); qr[i] = read();
        qu[s].push_back(i);
    }
    for(int i = n;i > 0;i --) {
        for(auto &p:bu[i]) {
            addc(p.FI,1); addc(p.SE+1,-1);
        }
        for(auto &x:qu[i]) {
            as[x] = sum(qr[x]);
        }
    }
    for(int i = 1;i <= m;i ++) {
        AIput(as[i],'\n');
    }
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值