洛谷P2633 Count on a tree

Description

给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。


Input

第一行两个整数N,M。

第二行有N个整数,其中第i个整数表示点i的权值。

后面N-1行每行两个整数(x,y),表示点x到点y有一条边。

最后M行每行两个整数(u,v,k),表示一组询问。


Output

M行,表示每个询问的答案。


Solution

二分套树或树套树别想了,没戏。
这里有一个常用trick:
两点路径上点权和=sum[x]+sum[y]-sum[lca(x,y)]-sum[fa[lca(x,y)]]
可以自己想想为什么。
那么按树结构建主席树,查询直接这样算即可。


Code

#include<bits/stdc++.h>
using namespace std;
map<long long,int>mp;
map<int,long long>mp1;
struct Edge{
    int v,nxt;
}e[400010];
int n,m,tot,sum,s,cnt;
int head[200010];
long long a[200010];
long long c[200010];
int fa[200010];
int id[200010];
int siz[200010];
int son[200010];
int dep[200010];
int top[200010];
int node[200010];
void addEdge(int u,int v){
	tot++;
    e[tot].v = v;
    e[tot].nxt = head[u];
    head[u] = tot;
}
inline void ad(int u,int v){
    addEdge(u,v);
    addEdge(v,u);
}
int tree[200010*40];
int ls[200010*40];
int rs[200010*40];
int rt[200010];
int build(int l,int r){
	int id=++cnt;
	int mid=(l+r)>>1;
	if(l==r) return id;
	ls[id]=build(l,mid);
	rs[id]=build(mid+1,r);
	return id;
}
int update(int pre,int l,int r,int x){
	int id=++cnt;
	ls[id]=ls[pre];
	rs[id]=rs[pre];
	tree[id]=tree[pre]+1;
	if(l==r) return id;
	int mid=(l+r)>>1;
	if(x<=mid) ls[id]=update(ls[pre],l,mid,x);
	else rs[id]=update(rs[pre],mid+1,r,x);
	return id;
}
int query(int LT,int RT,int lca,int lcaf,int l,int r,int k){
	if(l>=r) return l;
	int mid=(l+r)>>1;
	int sum=tree[ls[LT]]+tree[ls[RT]]-tree[ls[lca]]-tree[ls[lcaf]];
	if(sum>=k) return query(ls[LT],ls[RT],ls[lca],ls[lcaf],l,mid,k);
	else return query(rs[LT],rs[RT],rs[lca],rs[lcaf],mid+1,r,k-sum);
}
void dfs1(int u,int father,int depth){
	rt[u]=update(rt[fa[u]],1,s,mp[a[u]]);
	//cout<<u<<" "<<fa[u]<<endl;
    siz[u]=1;
    fa[u]=father;
    dep[u]=depth;
    int maxn=-1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==father) continue;
        fa[v]=u;
        dfs1(v,u,depth+1);
        siz[u]+=siz[v];
        if(siz[v]>maxn){
            maxn=siz[v];
            son[u]=v;
        }
    }
}
void dfs2(int u,int ltop){
    top[u]=ltop;
    if(!son[u]) return;
    dfs2(son[u],ltop);
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa[u]||v==son[u])
        continue;
        dfs2(v,v);
    }
}
int lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    return x;
}
int main(){
    int op,x,y,z;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
    	scanf("%lld",&a[i]);
    	c[i]=a[i];
	}
    sort(c+1,c+n+1);
    for(int i=1;i<=n;i++){
    	if(c[i]!=c[i-1]) s++;
    	mp[c[i]]=s;
    	mp1[s]=c[i];
	}
    for(int i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        ad(x,y);
    }
	rt[0]=build(1,s);
	dfs1(1,-1,1);
    dfs2(1,1);
    //cout<<"pre:ok\n";
	int las=0;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z); x^=las;
		printf("%lld\n",las=mp1[query(rt[x],rt[y],rt[lca(x,y)],rt[fa[lca(x,y)]],1,s,z)]);
	}
}
### 关于洛谷 P1609 的题目解析 目前并未提供具体的引用内容来描述洛谷 P1609 的相关信息。然而,基于常见的编程竞赛题型以及类似的动态规划或贪心算法问题,可以推测该题目可能涉及某种优化策略。 #### 假设的解法思路 如果假设洛谷 P1609 是一道关于资源分配或者路径寻找的问题,则可以通过以下方法解决: 1. **动态规划** 动态规划是一种常用的解决问题的方法,在许多情况下能够有效地找到最优解。例如,对于某些最大化收益或最小化成本的问题,定义状态转移方程是非常重要的[^3]。 ```cpp // 示例代码框架(假设为背包问题) #include <iostream> #include <vector> using namespace std; const int MAX_N = 1e3 + 5; int dp[MAX_N]; int main(){ int N, W; cin >> N >> W; vector<int> weight(N), value(N); for(int i=0;i<N;i++) cin>>weight[i]; for(int i=0;i<N;i++) cin>>value[i]; for(int i=0;i<N;i++){ for(int j=W;j>=weight[i];j--){ dp[j]=max(dp[j],dp[j-weight[i]]+value[i]); } } cout<<dp[W]<<endl; return 0; } ``` 2. **贪心算法** 如果问题是关于局部最优解的选择,那么贪心算法可能是更高效的方式。通过每次选择当前最佳选项逐步构建全局最优解[^4]。 ```cpp // 贪心算法示例代码(假设为区间覆盖问题) #include <bits/stdc++.h> using namespace std; struct Interval{ int start,end; }; bool cmp(const Interval& a,const Interval& b){ return a.end<b.end; } int main(){ int n; cin>>n; vector<Interval> intervals(n); for(auto &i:intervals) cin>>i.start>>i.end; sort(intervals.begin(),intervals.end(),cmp); int count=0,last=-1; for(auto &i:intervals){ if(last<i.start){ last=i.end; count++; } } cout<<count<<endl; return 0; } ``` #### 总结 虽然具体到洛谷 P1609 的细节尚未给出,但从上述分析可以看出,无论是动态规划还是贪心算法都可以作为通用解决方案的一部分。实际应用时需根据题目条件调整参数设置和逻辑流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值