Codeforces Round #121 (Div. 1) C. Fools and Roads 经典的lca前缀和问题
来源:互联网 发布:学手语的软件 编辑:程序博客网 时间:2024/05/23 16:38
题意:
给你一棵树,有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]; //保存每个点到树根的距离,很多问题中树边都有权值,会询问两点间的距离,//如果树边没权值,相当于权值为1void 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;}
0 0
- Codeforces Round #121 (Div. 1) C. Fools and Roads 经典的lca前缀和问题
- CodeForces 191C Fools and Roads 树上的前缀和 LCA
- Codeforces-191C: Fools and Roads(LCA)
- [CF 191C]Fools and Roads[LCA Tarjan算法][LCA 与 RMQ问题的转化][LCA ST算法]
- Codeforces 191 C Fools and Roads (树链剖分)
- Codeforces 191C Fools and Roads(树链剖分)
- Fools and Foolproof Roads CodeForces
- Codeforces Round #216 (Div. 2) D. Valera and Fools
- Codeforces Round #216 (Div. 2) D. Valera and Fools
- Codeforces Round #343 (Div. 2) E. Famil Door and Roads (树形dp,lca)
- Codeforces Round #343 (Div. 2) E. Famil Door and Roads (树形dp,lca)★ ★ ★
- Codeforces Round #400 (Div. 1 + Div. 2, combined) C. Molly's Chemicals 区间和、构造、前缀的后缀
- codeforces 192E Fools and Roads
- Codeforces 192E Fools and Roads【树链剖分】
- Codeforces Round #427 (Div. 2) C (前缀和思想)
- Codeforces Round #226 (Div. 2):Problem 385C - Bear and Prime Numbers (素数刷法+前缀和)
- 【Codeforces Round 354 (Div 2)C】【前缀和二分or双指针】Vasya and String ab序列最多改变k位置的最长同字符子串长度
- 【前缀和 && 思维转换】ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined)Molly's Chemicals
- second test
- JS循环执行函数setInterval
- JSON的定义
- 深入浅出如何解析xml文件---下篇
- 备份到远程服务器
- Codeforces Round #121 (Div. 1) C. Fools and Roads 经典的lca前缀和问题
- Android : DevicePolicyManager
- SqQueue循环队列的 顺序存储实现
- 约瑟夫环典型问题
- 集合
- ASP.NET Core 1.0: Deploy to IIS
- JavaScript高级程序学习笔记(一)表单脚本
- Java实现选择排序、插入排序、希尔排序算法
- delphi indy控件 idsmtp 邮件发送 gmail