套题T5//各种树
树(tree)
【题目描述】
方方方种下了三棵树,一年后,第一棵树长出了n个节点。
方方方会向你提出m个询问,每个询问给出两个数i,j,你需要回答i号节点和j号节点在树上的距离。
【输入数据】
第一行两个整数n,m。接下来n-1行每行两个整数a,b表示一条边。接下来m行每行两个整数i,j表示询问。
【输出数据】
m行,每行一个整数表示答案。
【样例输入】
3 2
1 2
1 3
3 2
1 1
【样例输出】
2
0
【数据范围】
对于30%的数据,n,m<=1000。
对于100%的数据,n,m<=500000。
裸lca
//lca教程 http://www.cnblogs.com/gc812/p/5839501.html
最小值是__min
答案就是f[i]-__min + f[j]-__min
path就没什么用了
先写RMQ 然后DFS的时候 这道题数据很大 保存成双向邻接表然后遍历一边就行了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <vector> #include <cstring> #include <map> using namespace std; typedef vector<vector<int> > vv; void get_path(vector<int>& path,const vv& t,const int& cur,bool* vis,int depth,vector<int>& t_depth,map<int, int>& fr) { for (vector<int>::const_iterator i=t[cur].begin(); i!=t[cur].end(); ++i) if (!vis[*i]) { fr[*i]=path.size(); path.push_back(*i); t_depth.push_back(depth+1); vis[*i]=1; get_path(path,t,*i,vis,depth+1,t_depth,fr); path.push_back(cur); t_depth.push_back(depth); vis[*i]=0; } return; } inline void swap(int& a,int& b) { int c=a; a=b; b=c; } class rmq { private: vector<vector<int> > f; void ini(const vector<int>&,const int&,const int&); int get_min(const int & a,const int& b) { return a<b?a:b; } public: rmq(const vector<int>& depth) { int n=depth.size(); if (n>0) { int k=0,temp=n-1; while (temp!=0) { ++k; temp>>=1; } f=vector<vector<int> >(n,vector<int>(k+1)); ini(depth,n,k); } } int query(const int& l,const int& r); }; void rmq::ini(const vector<int>& depth,const int& n,const int& k) { for (int i=0; i<n; ++i) f[i][0]=depth[i]; for (int j=1; j<=k; ++j) for (int i=0; i<n; ++i) if ((i+(1<<j-1))<n) f[i][j]=get_min(f[i][j-1],f[i+(1<<(j-1))][j-1]); } int rmq::query(const int& l,const int& r) { if (l==r) return f[l][0]; int temp=r-l,k=0; while (temp!=0) { temp>>=1; ++k; } return get_min(f[l][k-1],f[r+1-(1<<(k-1))][k-1]); } int main() { int n,m; scanf("%d%d",&n,&m); vv t(n+1,vector<int>()); for (int i=0; i<n-1; ++i) { int a,b; scanf("%d%d",&a,&b); t[a].push_back(b); t[b].push_back(a); } vector<int> path(1,1),t_depth(1,1); bool* vis=new bool[n+1]; memset(vis,0,n+1); vis[1]=1; map<int, int> fr; fr[1]=0; get_path(path,t,1,vis,1,t_depth,fr); rmq RMQ(t_depth); for (int i=0; i<m; ++i) { int a,b; scanf("%d%d",&a,&b); int low=fr[a],high=fr[b]; if (low>high)swap(low,high); int min=RMQ.query(low,high); printf("%d\n",t_depth[fr[a]]-min+t_depth[fr[b]]-min); } return 0; }
树(tree2)
【题目描述】
方方方种下了三棵树,两年后,第二棵树长出了n个节点,其中1号节点是根节点。
方方方进行m次操作,每个操作为:
(1)给出两个数i,x,将第i个节点的子树中,与i距离为斐波那契数的节点权值+x(包括i本身)。
(2)给出一个数i,求出第i个节点的子树中,与i距离为斐波那契数的节点的权值和(包括i本身)。
【读入数据】
第一行两个整数n,m。接下来n-1行每行两个整数a,b表示一条边。接下来m行每行第一个数表示操作类型,接下来1或2个数表示i (,x)。
【读出数据】
对于每个(2)操作,输出一行一个整数表示答案。
【样例输入】
5 3
1 2
2 3
3 4
4 5
1 1 1
1 2 2
2 4
【样例输出】
1
【数据范围】
对于30%的数据,n,m<=1000。
对于100%的数据,n,m<=100000,|x|<=10^9。
树(tree3)
【题目描述】
方方方种下了三棵树,两年后,第二棵树长出了n个节点,其中1号节点是根节点。
方方方使用魔法为每个节点按以下规则染色:
(1) 每个节点为红色,黑色或白色。
(2) 对于每个叶节点i,有ri,bi,wi三个参数,满足ri+bi+wi=1。方方方分别以ri,bi,wi的概率给i染上红色,黑色和白色。
(3) 对于每个非叶节点,设它的子树大小为x,当它的子树中的其它x-1个节点都被染色后,对它进行染色。假设这x-1个节点分别有r,b,w个红色、黑色和白色,那么它被染成红色、黑色、白色的概率分别为r/(x-1),b/(x-1),w/(x-1)。
染色结束后,方方方按以下规则计算这棵树的魔法值:
(1) 对于每个有序点对(i,j),如果它们的颜色集合为{红色,黑色}且i是j的祖先,魔法值+rb。
(2) 对于每个有序点对(i,j),如果它们的颜色集合为{红色,白色}且i是j的祖先,魔法值+rw。
(3) 对于每个有序点对(i,j),如果它们的颜色集合为{黑色,白色}且i是j的祖先,魔法值+bw。
你需要求出魔法值的期望对998244353取模的结果。
【读入数据】
第一行四个整数n,rb,rw,bw,接下来n-1行每行两个整数a,b表示一条边。接下来n行每行三个整数ri,bi,wi,如果i是叶子节点,保证ri+bi+wi=1,否则ri=bi=wi=0。
【读出数据】
输出魔法值的期望对998244353取模的结果。
【样例读入】
2 1 2 3
1 2
1 0 0
499122177 499122177 0
【样例输出】
499122177
【数据范围】
对于10%的数据,n<=10。
对于30%的数据,n<=50。
对于另外20%的数据,rb=rw=bw。
对于再另外20%的数据,每个叶子节点i满足ri=bi=wi。
对于100%的数据,n<=1000,0<=rb,rw,bw,ri,bi,wi<998244353。