zoukankan      html  css  js  c++  java
  • [圆方树] Luogu P4630 Duathlon 铁人两项

    题目描述

    比特镇的路网由 mm 条双向道路连接的 nn 个交叉路口组成。

    最近,比特镇获得了一场铁人两项锦标赛的主办权。这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段赛程。

    比赛的路线要按照如下方法规划:

    1. 先选择三个两两互不相同的路口 s, cs,c和 ff,分别作为比赛的起点、切换点(运动员在长跑到达这个点后,骑自行车前往终点)、终点。
    2. 选择一条从 ss出发,经过 cc最终到达 ff的路径。考虑到安全因素,选择的路径经过同一个点至多一次。

    在规划路径之前,镇长想请你帮忙计算,总共有多少种不同的选取 s, cs,c和 ff的方案,使得在第 22步中至少能设计出一条满足要求的路径。

    题解

    • 把圆方树建出来,在树中任意枚举两个圆点作为s和f,然后考虑c有多少种选法。

    • 令每个圆点的权值为-1,每个方点的权值为点双大小,那么选法就是两点路径的权值和

    • 也就是说我们要求圆方树上n^2条圆点到圆点的路径的权值和,很容易想到计算每个点被算了多少次

    代码

     1 #include <cstdio>
     2 #include <iostream>
     3 #define N 400010
     4 using namespace std;
     5 int n,m,tot,cnt,num,sum,head[N],last[N],stack[N],sz[N],val[N],dfn[N],low[N];
     6 long long ans;
     7 struct edge {int to,from;}e[N],E[N];
     8 void insert(int x,int y)
     9 {
    10     e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt;
    11     e[++cnt].to=x,e[cnt].from=head[y],head[y]=cnt;
    12 }
    13 void ins(int x,int y){ E[++num].to=y,E[num].from=last[x],last[x]=num; }
    14 void tarjan(int x)
    15 {
    16     dfn[x]=low[x]=++dfn[0],stack[++stack[0]]=x,sz[x]=1,val[x]=-1;
    17     for (int i=head[x],l;i;i=e[i].from)
    18         if (!dfn[e[i].to])
    19         {
    20             tarjan(e[i].to),low[x]=min(low[x],low[e[i].to]);
    21             if (low[e[i].to]>=dfn[x])
    22             {
    23                 ins(x,++tot),val[tot]=1,l=0;
    24                 do{ l=stack[stack[0]--],ins(tot,l),sz[tot]+=sz[l],++val[tot]; }while (l!=e[i].to);
    25                 sz[x]+=sz[tot];
    26             }
    27         }
    28         else low[x]=min(low[x],dfn[e[i].to]);
    29 }
    30 void dfs(int x)
    31 {
    32     if (x<=n) ans+=1ll*(sum-1)*val[x];
    33     ans+=1ll*(sum-sz[x])*sz[x]*val[x];
    34     for (int i=last[x];i;i=E[i].from) ans+=1ll*(sum-sz[E[i].to])*sz[E[i].to]*val[x],dfs(E[i].to); 
    35 }
    36 int main()
    37 {
    38     scanf("%d%d",&n,&m),tot=n;
    39     for (int x,y;m;m--) scanf("%d%d",&x,&y),insert(x,y);
    40     for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i),sum=sz[i],dfs(i);
    41     printf("%lld",ans);
    42  } 
  • 相关阅读:
    CCF NOI1062 计算曼哈顿距离
    CCF NOI1061 Wifi密码
    CCF NOI1073 放苹果
    CCF NOI1072 爬楼梯
    CCF NOI1071 Pell数列
    CCF NOI1064 计算斐波那契第n项
    NOI训练行动路线图
    CCF NOI1060 雷同检测
    CCF NOI1059 表达式求值
    CCF NOI1057 石头剪刀布
  • 原文地址:https://www.cnblogs.com/Comfortable/p/11281783.html
Copyright © 2011-2022 走看看