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
原创粉丝点击