动态链上第k小——整体二分+ds:P4175

https://www.luogu.com.cn/problem/P4175

第k大先转第k小。

考虑离线。离线完后此题采用整体二分,对于当前二分区间是独立的。把所以操作的查询按时间戳排序。

对于修改操作,如果当前点上的值小于等于mid,则可以有贡献。

对于查询操作,要判断当前两点链上有多少个点有权值。可以用树上差分来解决,所以上一步的维护就要支持子树加,所以转dfs序就是区间加单点求值,树状数组可维护。

如果当前有点的权值数量太多,说明答案小于等于mid,那就丢到左边去二分。如果太少,说明答案大于mid,那么久先减去当前二分区间的贡献, 再丢到右边。

pre coding at 14:26
st coding at 16:21
st bugging at 18:21
passing at 18:51
fn blogging at 19:00
#include<bits/stdc++.h>
using namespace std;
#ifdef LOCALd
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
#endif
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||
ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//srand(time(0));
#define N 80010
//#define M
//#define mo
struct node {
	int op, x, y, t, k; 
};
vector<node>s;  
int n, m, i, j, k, T;
int ans[N], f[N][22], L[N], R[N], dfn[N], dep[N]; 
int tot, q, u, v, a[N], x, y, len; 
vector<int>G[N]; 

void dfs(int x, int fa) {
	dep[x]=dep[fa]+1; f[x][0]=fa; 
	dfn[x]=L[x]=++tot; 
	for(int y : G[x]) {
		if(y == fa) continue; 
		dfs(y, x); 
	}
	R[x]=tot; 
}

int lca(int x, int y) {
	if(x==y) return x; 
	if(dep[x] < dep[y]) swap(x, y); 
	for(int k=20; k>=0; --k)
		if(dep[f[x][k]]>=dep[y]) x=f[x][k]; 
	if(x==y) return x; 
	for(int k=20; k>=0; --k) 
		if(f[x][k] != f[y][k]) x=f[x][k], y=f[y][k]; 
	return f[x][0]; 
}

struct Binary_tree {
	int cnt[N]; 
	void add(int x, int k) {
		while(x<N) cnt[x]+=k, x+=x&-x; 
	}
	int que(int x) {
		int ans=0; 
		while(x) ans+=cnt[x], x-=x&-x; 
		return ans; 
	}
	int calc(int x, int y) {
		int z = lca(x, y); 
//		debug("calc(%d %d) => %d | %d %d %d %d\n", x, y, que(dfn[x])+que(dfn[y])-que()-que(f[z][0]), que(dfn[x]), que(dfn[y]), qye(z), ); 
		return que(dfn[x])+que(dfn[y])-que(dfn[z])-que(dfn[f[z][0]]); 
	}
	void add(int l, int r, int k) {
		add(l, k); add(r+1, -k); 
	}
}Bin;

void solve(int l, int r, vector<node>s) {
	if(l==r) {
		for(auto t : s) if(t.op==2) ans[t.t]=l; 
		return ; 
	}
	int mid = (l + r) >> 1; 
	auto cmp = [&] (node x, node y) -> bool {
		if(x.t == y.t) return x.op < y.op; 
		return x.t < y.t; 
	}; 	
	sort(s.begin(), s.end(), cmp); 
	debug("######$$####### [%d %d] mid %d\n", l, r, mid); 
//	for(auto t : s) debug("%d(op) %d(x) %d(y) %d(t) %d(k)\n", t.op, t.x, t.y, t.t, t.k); 
	int x, k; 
	vector<node>va, vb; 
	for(auto t : s) {
		debug("%d(op) %d(x) %d(y) %d(t) %d(k)\n", t.op, t.x, t.y, t.t, t.k); 
		if(t.op==1) {
			if(t.y <= mid) {
				va.pb(t); x=t.x; 
				debug("Add[%d %d] %d\n", L[x], R[x], x); 
				Bin.add(L[x], R[x], t.k);
			}
			else vb.pb(t); 
		}
		else {
			k = Bin.calc(t.x, t.y); 
			if(k >= t.k) va.pb(t); 
			else t.k-=k, vb.pb(t); 
		}
	}
	for(auto t : va) if(t.op==1) Bin.add(L[t.x], R[t.x], -t.k); 
	if(!va.empty()) solve(l, mid, va); 
	if(!vb.empty()) solve(mid+1, r, vb); 
} 

signed main() {
	#ifdef LOCALs
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	T=read();
//	while(T--) {
//
//	}
	n=read(); q=read(); 
	for(i=1; i<=n; ++i) { a[i]=read(); s.pb({1, i, a[i], 1, 1}); }
	for(i=1; i<n; ++i) { u=read(); v=read(); G[u].pb(v); G[v].pb(u); }
	dfs(1, 0); 
	for(k=1; k<=20; ++k) for(i=1; i<=n; ++i) f[i][k]=f[f[i][k-1]][k-1]; 
	debug("dep : "); for(i=1; i<=n; ++i) debug("%d ", dep[i]); debug("\n"); 
	for(i=1; i<=q; ++i) {
		k=read(); x=read(); y=read(); 
		if(k==0) {
			s.pb({1, x, a[x], i, -1}); 
			a[x]=y; 
			s.pb({1, x, a[x], i, 1}); 
		}
		else {
			len=dep[x]+dep[y]-2*dep[lca(x, y)]+1; 
//			debug("Len (%d <-> %d) %d\n", x, y, len); 
			debug("%d %d | %d\n", x, y, len-k+1); 
			if(k > len) ans[i] = -1; 
			else s.pb({2, x, y, i, len-k+1}); 
		}
	}
//	solve(1, 10, s); 
	solve(1, 1e8, s); 
	for(i=1; i<=q; ++i) 
		if(!ans[i]) continue; 
		else if(ans[i]==-1) printf("invalid request!\n"); 
		else printf("%d\n", ans[i]); 
	return 0;
}


  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值