洛谷P10391 [蓝桥杯 2024 省 A] 零食采购
洛谷P10391 [蓝桥杯 2024 省 A] 零食采购
洛谷题目传送门
题目描述
小蓝准备去星际旅行,出发前想在本星系采购一些零食,星系内有 nnn 颗星球,由 n−1n-1n−1 条航路连接为连通图,第 iii 颗星球卖第 cic_ici 种零食特产。小蓝想出了 qqq 个采购方案,第 iii 个方案的起点为星球 sis_isi ,终点为星球 tit_iti ,对于每种采购方案,小蓝将从起点走最短的航路到终点,并且可以购买所有经过的星球上的零食(包括起点终点),请计算每种采购方案最多能买多少种不同的零食。
输入格式
输入的第一行包含两个正整数 nnn,qqq,用一个空格分隔。
第二行包含 nnn 个整数 c1,c2,⋯,cnc_1,c_2,\cdots, c_nc1,c2,⋯,cn,相邻整数之间使用一个空格分隔。
接下来 n−1n - 1n−1 行,第 iii 行包含两个整数 ui,viu_i,v_iui,vi,用一个空格分隔,表示一条
航路将星球 uiu_iui 与 viv_ivi 相连。
接下来 qqq 行,第 iii 行包含两个整数 $s_i
, t_i $,用一个空格分隔,表示一个采购方案。
输出格式
输出 qqq 行,每行包含一个整数,依次表示每个采购方案的答案。
输入输出样例 #1
输入 #1
4 2
1 2 3 1
1 2
1 3
2 4
4 3
1 4
输出 #1
3
2
说明/提示
第一个方案路线为 {4,2,1,3}\{4, 2, 1, 3\}{4,2,1,3},可以买到第 1,2,31, 2, 31,2,3 种零食;
第二个方案路线为 {1,2,4}\{1, 2, 4\}{1,2,4},可以买到第 1,21, 21,2 种零食。
对于 20% 的评测用例,$1 ≤ n, q ≤ 5000 $;
对于所有评测用例,1≤n,q≤105,1≤ci≤20,1≤ui,vi≤n,1≤si,ti≤n1 ≤ n, q ≤ 10^5,1 ≤ c_i ≤ 20,1 ≤ u_i , v_i ≤ n,1 ≤ s_i , t_i ≤ n1≤n,q≤105,1≤ci≤20,1≤ui,vi≤n,1≤si,ti≤n。
思路详解
确定算法
首先看到数据范围,由于1≤ci≤201 ≤ c_i ≤ 201≤ci≤20,那我们显然可以使用状态压缩,使用一个二进制数保存买了哪几种零食。
由于这是一棵树,又要我们查询一条路径上的信息,那我们显然可以使用树链剖分。
在考虑用什么数据结构维护,由于要维护区间或,那直接使用FHQ Treap即可。
具体步骤
按照题目的要求直接模拟即可。
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=4e5+5;
struct lit{int val,pri,siz,sor;lit *l,*r;//sor维护区间或lit(int v){val=sor=v;siz=1;pri=rand();l=r=nullptr;}
};
class FHQ_Treap{//FHQ模板
private:lit *root;int getsize(lit *u){return u?u->siz:0;}int getsor(lit *u){return u?u->sor:0;}void pushup(lit *&u){if(!u)return;u->siz=1+getsize(u->l)+getsize(u->r);u->sor=u->val|getsor(u->l)|getsor(u->r);}void split(lit *u,int k,lit *&x,lit *&y){if(!u){x=y=nullptr;return;}int ls=getsize(u->l);if(ls+1<=k){x=u;split(u->r,k-ls-1,x->r,y);pushup(x);}else {y=u;split(u->l,k,x,y->l);pushup(y);}}lit *merge(lit *x,lit *y){if(!x)return y;if(!y)return x;if(x->pri>y->pri){x->r=merge(x->r,y);pushup(x);return x;}else {y->l=merge(x,y->l);pushup(y);return y;}}
public:void push_back(int v){root=merge(root,new lit(v));}int query(int le,int ri){//查寻区间或lit *l,*mid,*r;split(root,ri,mid,r);split(mid,le-1,l,mid);int res=getsor(mid);root=merge(merge(l,mid),r);return res;}
}tree;
ll h[N],to[N],ne[N],tot;
void add(ll u,ll v){tot++;to[tot]=v;ne[tot]=h[u];h[u]=tot;
}
ll dep[N],fa[N],son[N],siz[N];
ll id[N],reid[N],top[N],tim=0;
ll n,m,a[N];
class Heavy_Chain_Descomposition{//树链剖分模板
private:void dfs1(ll u,ll fath,ll dp){dep[u]=dp;fa[u]=fath;siz[u]=1;for(ll i=h[u];i;i=ne[i]){ll v=to[i];if(v==fath)continue;dfs1(v,u,dp+1);siz[u]+=siz[v];if(siz[v]>siz[son[u]])son[u]=v;}}void dfs2(ll u,ll fath,ll ori){id[u]=++tim;reid[tim]=u;top[u]=ori;if(son[u])dfs2(son[u],u,ori);for(ll i=h[u];i;i=ne[i]){ll v=to[i];if(v==fath||v==son[u])continue;dfs2(v,u,v);}}
public:void build(ll root,ll n){dfs1(root,0,1);dfs2(root,0,root);for(int i=1;i<=n;i++)tree.push_back(a[reid[i]]);}int find(int u,int v){//查询路径(u,v)之间的区间或int res=0;while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]])swap(u,v);res|=tree.query(id[top[u]],id[u]);u=fa[top[u]];}if(id[u]>id[v])swap(u,v);res|=tree.query(id[u],id[v]);return res;}
}tr;
ll s[N];
int lowbit(int x){return x&(-x);}//最后一个1的次幂,参考树状数组
int getans(int x){//统计二进制下有多少个1int ans=0;while(x){x-=lowbit(x);ans++;}return ans;
}
int main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>n>>m;for(int i=1;i<=n;i++){int x;cin>>x;a[i]=1<<x;}for(int i=1;i<=n-1;i++){int x,y;cin>>x>>y;add(x,y);add(y,x);}tr.build(1,n);//记得建树for(int i=1;i<=m;i++){int x,y;cin>>x>>y;cout<<getans(tr.find(x,y))<<'\n';}return 0;
}