题意:
给你一棵树,有n个点,并按顺序给你n-1条边, 然后有m次操作,每一次操作输入两个点a,b表示从a走到b点, 把路径中的所有边都染色一遍, 最后按顺序输出所有边被染色的次数。
第一眼看这道题就感觉这是一个很经典的问题。 首先我们知道对于a,b必然先找出ab的lca。 然后 朴素想法: 从a 向上到lca,从b向上到lca ,每条边的flag 都++,最后输出, 这种做法肯定是TLE 的,n=10W的数据量不会给你这么水过去。
那么我们考虑 先把所有的操作都做完,用什么东西记录下结果,然后再依次输出。
在纸上画一画再 思考一下可以发现前缀和可以解决此问题:(因为根节点到每一个节点只有一条路)
a->lca->b 上的边都会+1, 加入我们设 ans[i] 表示从根节点到i节点的上的所有边 染色的次数。 那么对于一次操作我们可以让: ans[a]++,ans[b]++, ans[lca]-2; ,表示从根节点到a节点上所有边都被染色一边,自然lca往上的边 都需要-2;
那么操作完之后我们如何去计算了?
自然我们需要按深度从大到小来计算边的染色次数。 我是先把所有的点按深度sort了一遍,然后对于以某个点v为终点的边 进行计算, 然后将ans[v]加到起点u上: ans[u]+=ans[v];
/*
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
.' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' | |
\ .-\__ `-` ___/-. /
___`. .' /--.--\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
佛祖保佑 永无BUG
*/
/*
* LCA 在线算法:dfs +ST(RMQ)
* poj 1330 裸题
!!!!!!!!!!!!!!!!!!!!!!!!!! 求 i 深度 : rmq[first[i]]
!!!!!!!!!!!!!!!!!!!!!!!!!! 求 i 深度 : rmq[first[i]]
!!!!!!!!!!!!!!!!!!!!!!!!!! 求 i 深度 : rmq[first[i]]
*/
#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <typeinfo>
#include <fstream>
#include <map>
#include <stack>
typedef long long ll;
using namespace std;
const int MAXN=100010;
int rmq[2*MAXN]; //rmq数组, 记录每个节点在树中的深度
struct ST{
int mm[2*MAXN];
int dp[2*MAXN][20]; //dp 直接存下标
void init(int n){
mm[0]=-1;
for(int i=1;i<=n;i++){
mm[i]=((i&(i-1))==0)? mm[i-1]+1 :mm[i-1]; //???莫名其妙定义长度
dp[i][0]=i; //初始化dp数组
}
for(int j=1;j<=mm[n];j++) //可以看出来mm[n] 就是一个长度为n的序列j的最大值
for(int i=1;i+(1<<j)-1 <=n ; i++){
if(rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1] ]){
dp[i][j]=dp[i][j-1];
}
else
dp[i][j]=dp[i+(1<<(j-1))][j-1];
}
}
int query(int a,int b){ //这个询问返回的是位置pos, F[pos]才是值
if(a>b)
swap(a,b);
int k=mm[b-a+1];
if(rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]] ){
return dp[a][k];
}
else
return dp[b-(1<<k)+1][k];
}
};
ST st;
struct Edge{
int to,next;
int num;
};
Edge edge[MAXN*2];
int tot,head[MAXN];
int F[MAXN*2];
int first[MAXN];
int cnt;
int fa[MAXN]; // 将每个点的 fa 设位 末端为 i 的边 的 编号 tot
//int dir[N]; //保存每个点到树根的距离,很多问题中树边都有权值,会询问两点间的距离,
//如果树边没权值,相当于权值为1
void addedge(int u,int v){ //无向边自然加两次
edge[tot].to=v;
edge[tot].next=head[u];
edge[tot].num=0;;
head[u]=tot++;
}
void dfs(int u,int pre,int dep){ //u起点,dep表示深度
F[++cnt] = u; // cnt就是这个节点的下标
rmq[cnt]=dep; //rmq记录节点在树中的深度
first[u]=cnt; //这个first 不会被更新掉,因为我们把continue了回去的路
for(int i=head[u];i!=-1; i=edge[i].next){
int v=edge[i].to;
if(v==pre) continue;
if(fa[v]==-1) fa[v]=i; // 将每个点的 fa 设位 末端为 i 的边 的 编号 tot
// printf("fa:%d %d %d\n",v,fa[v],u);
dfs(v,u,dep+1);
F[++cnt] = u; //虽然continue回去的路,但是询问叶子节点还要返回:1-2-1 --....
rmq[cnt] = dep;
}
}
void LCA_init(int root ,int node_num){ //查询LCA前的初始化
cnt=0;
dfs(root,root,0);
st.init(2*node_num-1); // 注意 ,这里dp的不是n,而是对F进行dp,长度2n-1
}
int query_lca(int u,int v){ //查询lca(u,v)
return F[st.query(first[u],first[v])];
}
bool flag[MAXN];
__int64 sum[MAXN*2];
__int64 ans[MAXN*2]; //真的蠢 ,不开二倍空间,真脑残
void init(){
tot=0;
memset(head,-1,sizeof(head));
memset(flag,0,sizeof(flag));
memset(sum,0,sizeof(sum));
memset(ans,0,sizeof(ans));
memset(fa,-1,sizeof(fa));
}
int pos[MAXN];
bool cmp(int a,int b){
return rmq[first[a]]>rmq[first[b]];
}
int main(){
//freopen("1.txt","r",stdin);
int N;
int u,v;
scanf("%d",&N);
init();
for(int i=1;i<=N;i++) pos[i]=i;
for(int i=1;i<N;i++){
scanf("%d %d",&u,&v);
addedge(u,v);
addedge(v,u);
flag[v]=true;
}
int root;
for(int i=1;i<=N;i++)
if(!flag[i]){ //某个根
root=i;
break;
}
LCA_init(root,N);
int q;
scanf("%d",&q);
while(q--){
scanf("%d %d",&u,&v);
int LCA=query_lca(u,v);
sum[u]++;
sum[v]++;
sum[LCA]-=2;
}
sort(pos+1,pos+N+1,cmp);
// for(int i=1;i<=N;i++){
// printf("%d %d\n",pos[i],sum[pos[i]]);
// }
for(int k=1;k<=N;k++){
int end=pos[k];
int i=fa[end];
int sta=edge[i^1].to;
if(i%2==1) i--;
// printf("%d %d %d\n",sta,end,i);
sum[sta]+=sum[end];
if(end!=root) ans[i]=sum[end];
}
for(int i=0;i<2*(N-1);i+=2){
printf("%I64d ",ans[i]);
}
printf("\n");
return 0;
}