AT_joisc2018_a

思路

看到这种区间覆盖可以优先考虑 ODT 了,因为每次它都覆盖的是一条路径那么考虑每次用 ODT 来修改,因为 ODT 需要时一个连续的区间所以直接套一个重链剖分即可,然后我们将询问离线,然后对于每一次询问按照通过树链剖分去跳链,然后将其加入树状数组中然后再将查询答案累积到答案中即可。

注意,因为树链剖分的每一个小区间是从前往后遍历的,而树剖又是整体从下往上跳的,故去要将其中一个反着遍历,时间复杂度大概是 \(O(n\log(n)^3)\),成功成为目前最劣解。

代码

#include <bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include <ext/rope>
using namespace __gnu_pbds;
using namespace std;
#define pb push_back
#define rep(i,x,y) for(register int i=x;i<=y;i++)
#define rep1(i,x,y) for(register int i=x;i>=y;--i)
#define in(x) scanf("%lld",&x)
#define int long long
#define fire signed
#define il inline
il void print(int x) {
	if(x<0) putchar('-'),x=-x;
	if(x>=10) print(x/10);
	putchar(x%10+'0');
}
int T=1;
int n;
struct node{
	int l,r;
	mutable int val;
	friend bool operator<(const node&a,const node&b) {
		return a.l<b.l;
	}
};
const int N=1e5+10;
vector<int>v[N];
set<node>s;
auto split(int pos) {//ODT
	auto it=s.lower_bound({pos,0,0});
	if(it!=s.end()&&it->l==pos) return it;
	it--;
	if(it->r<pos) return s.end();
	int l=it->l,r=it->r,v=it->val;
	s.erase(it);
	s.insert({l,pos-1,v});
	return s.insert({pos,r,v}).first;
}
void add(int l,int r,int c) {
	auto itr=split(r+1),itl=split(l);
	s.erase(itl,itr);
	s.insert({l,r,c});
}
int a[N],b[N];
int ans[N];
int dfn[N],son[N],siz[N],f[N],dep[N],top[N];
void dfs(int x,int fa) {
	f[x]=fa;
	dep[x]=dep[fa]+1;
	siz[x]=1;
	for(auto to:v[x]) {
		if(to==fa) continue;
		dfs(to,x);
		siz[x]+=siz[to];
		if(siz[to]>siz[son[x]]) son[x]=to;
	}
}
int idx,mp[N];
void dfs1(int x,int head) {
	top[x]=head;
	dfn[x]=++idx; 
	mp[idx]=x;
	if(!son[x]) return ;
	dfs1(son[x],head);
	for(auto to:v[x]) if(!dfn[to]) dfs1(to,to);
}
int res[N];
int lowbit(int x) {
	return x&-x;
}
int tr[N];
void add(int x,int k) {
	for(;x<=n;x+=lowbit(x)) tr[x]+=k;
}
int Ans(int x) {
	int res=0;
	for(;x;x-=lowbit(x)) res+=tr[x];
	return res;
}
void modify(int x,int y,int &res) {
	auto itr=split(y+1),itl=split(x);
	itr--;
	itl--;
	auto it=itr;
	for(;itl!=itr;itr--) {
		int cnt=itr->r-itr->l+1;
		res+=cnt*Ans(itr->val-1);
		add(itr->val,cnt);
	}
}
void modify1(int x,int y) {
	auto itr=split(y+1),itl=split(x);
	itr--;
	itl--;
	auto it=itr;
	for(;itl!=itr;itr--) {
		int cnt=itr->r-itr->l+1;
		add(itr->val,-cnt);
	}
}
int Ans(int x,int y) {
	int res=0;
	int xx=x,yy=y;
	while(top[x]!=top[y]) {//树链剖分
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		modify(dfn[top[x]],dfn[x],res);
		x=f[top[x]];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	modify(dfn[x],dfn[y],res);
	x=xx,y=yy;
	while(top[x]!=top[y]) {//清空
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		modify1(dfn[top[x]],dfn[x]);
		x=f[top[x]];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	modify1(dfn[x],dfn[y]);
	return res;
}
void gai(int x,int y,int k) {
	while(top[x]!=top[y]) {
		if(dep[top[x]]<dep[top[y]])  swap(x,y);
		add(dfn[top[x]],dfn[x],k);
		x=f[top[x]];
	}
	if(dfn[x]>dfn[y]) swap(x,y);
	add(dfn[x],dfn[y],k);
}
int tt[N];
void solve() {
	in(n);
	rep(i,1,n) in(a[i]),b[i]=a[i];
	sort(b+1,b+1+n);
	int m=unique(b+1,b+1+n)-b-1;
	rep(i,1,n) a[i]=lower_bound(b+1,b+1+m,a[i])-b;//离散化
	rep(i,1,n-1) {
		int x,y;
		in(x),in(y);
		v[x].pb(y);
		v[y].pb(x);
		ans[i]=x;
		tt[i]=y;
	}
	dfs(1,0);
	dfs1(1,1);
	rep(i,1,n) s.insert({i,i,a[mp[i]]});
	rep(i,1,n-1) res[i]=Ans(1,ans[i]),gai(1,ans[i],a[tt[i]]);
	rep(i,1,n-1) printf("%lld\n",res[i]);
}
fire main() {
	while(T--) {
		solve();
	}
	return false;
}

posted @ 2024-08-19 16:04  highkj  阅读(5)  评论(0)    收藏  举报