BZOJ 5463: [APIO2018] 铁人两项 圆方树+树形DP

本文深入探讨了圆方树算法,一种处理图连通性问题的有效方法。通过将图的双连通分量转化为方点,原图点转化为圆点,形成树状结构,解决了在特定条件下计算不同点对间路径上点数的问题。文章详细讲解了算法原理,提供了状态转移方程,并附带代码实现。

title

BZOJ 5463
LUOGU 4630
Description

比特镇的路网由 m 条双向道路连接的 n 个交叉路口组成。
最近,比特镇获得了一场铁人两项锦标赛的主办权。这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段赛程。
比赛的路线要按照如下方法规划:
1、先选择三个两两互不相同的路口 s, c 和 f ,分别作为比赛的起点、切换点(运动员在长跑到达这个点后,骑自行车前往终点)、终点。
2、选择一条从 s 出发,经过 c 最终到达 f 的路径。考虑到安全因素,选择的路径经过同一个点至多一次。
在规划路径之前,镇长想请你帮忙计算,总共有多少种不同的选取 s, c 和 f 的方案,使得在第 2 步中至少能设计出一条满足要求的路径。

Input

第一行包含两个整数 n 和 m ,分别表示交叉路口和双向道路的数量。
接下来 m 行,每行两个整数 v_i, u_i 。表示存在一条双向道路连接交叉路口 v_i, u_i (1 <= v_i, u_i <= n, v_i != u_i)。
保证任意两个交叉路口之间,至多被一条双向道路直接连接。
n<=1e5, m<=2e5

Output

输出一行,包含一个整数,表示能满足要求的不同的选取s,c和f的方案数

Sample Input

4 3
1 2
2 3
3 4

Sample Output

8

Source

鸣谢Qingyu上传

analysis

圆方树就是,将图的每个点双连通分量建一个方点,把连通分量里的点全部连向这个方点,形成一棵树,原图中的点为圆点。
圆方树能处理与图连通性有关的许多问题。

啊,概念讲完了,下面回归本问题(一句话题意):

对于所有的有序点对 \((x,y),x\not= y\)\(\sum_{(x,y)}\) 可能出现在 \(x\)\(y\) 的简单路径上的点数,不包括 \(x\)\(y\)

分情况讨论可能出现在 \(x\)\(y\) 的简单路径上的点数(不包括 \(x\)\(y\) ):

  1. \(x\)\(y\) 在同一个点双内:为所在的点双大小减 \(2\)
  2. \(x\)\(y\) 都是割点且不在同一点双: \(x\)\(y\) 的路径上(不包括 \(x\)\(y\) )的点双大小之和(注:除 \(x\)\(y\) 之外的割点只能被统计一次)。
  3. \(x\)\(y\) 不在同一点双并且都不是割点: \(x\)\(y\) 的路径上的所有点双大小之和减去(路径上的点双个数加一)。

综上,我们把方点的权值设为对应点双大小,圆点的权值为 \(-1\) ,那么可能出现在 \(x\)\(y\) 的简单路径上的点数(不包括 \(x\)\(y\) )就是圆方树 \(x\)\(y\) 的路径上点的权值之和。
于是,我们把问题转化成一棵树上所有有序圆点对两两路径权值和之和

  • 状态:
    \(f[x]\) 表示 \(x\) 的子树内无序圆点对的路径权值和之和。
    \(g[x]\) 表示 \(x\)\(x\) 的子树内所有圆点的路径权值和之和。
    \(sum[x]\) 表示 \(x\) 的子树内圆点的个数。
    \(val[x]\) 表示 \(x\) 点的权值。
  • 转移:
    \(sum[x]=[x是圆点]+\sum_{y\in son[x]}sum[y]\)
    \(g[x]=\sum_{y\in son[x]}\{g[y]+sum[y]\times val[x]\}\)
    在枚举子树 \(y\) 的过程中记录下 \(g[x]\)\(sum[x]\) 表示 \(y\) 之前的子树(不包括 \(y\) )的 \(dp\) 值:\(f[x]+=f[y]+g[x]×sum[y]+g[y]×sum[x]\)

注意图可能不连通,所以答案为:\(2\times\sum_{i是某个连通块的根}f[i]\)
总复杂度 \(O(n+m)\)

参考资料:xyz32768

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5+10;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}

template<typename T>inline void write(T x)
{
    if (!x) { putchar('0'); return ; }
    if (x<0) putchar('-'), x=-x;
    T num=0, ch[20];
    while (x) ch[++num]=x%10+48, x/=10;
    while (num) putchar(ch[num--]);
}

struct Graph
{
    int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
    Graph()
    {
        memset(head,0,sizeof(head));
        len=0;
    }

    inline void add(int x,int y)
    {
        ver[++len]=y,Next[len]=head[x],head[x]=len;
        ver[++len]=x,Next[len]=head[y],head[y]=len;
    }
} G1,G2;

int dfn[maxn],low[maxn],id;
int Stack[maxn],top;
int val[maxn],belong[maxn],siz[maxn],tot;
inline void tarjan(int x)
{
    dfn[x]=low[x]=++id;
    Stack[++top]=x;
    for (int i=G1.head[x]; i; i=G1.Next[i])
    {
        int y=G1.ver[i];
        if (!dfn[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if (low[y]>=dfn[x])
            {
                int k;
                ++tot;
                do
                {
                    k=Stack[top--];
                    ++siz[tot];
                    G2.add(tot,k);
                } while (k!=y);
                ++siz[tot];
                G2.add(tot,x);
            }
        }
        else low[x]=min(low[x],dfn[y]);
    }
}

int n,m,sum[maxn];
long long ans,f[maxn],g[maxn];
inline void dfs(int x,int fa)
{
    sum[x]=(x<=n);
    g[x]=x<=n?-1:0;
    for (int i=G2.head[x]; i; i=G2.Next[i])
    {
        int y=G2.ver[i];
        if (y==fa) continue;
        dfs(y,x);
        f[x]+=f[y]+g[x]*sum[y]+g[y]*sum[x];//f[x] 表示 x 的子树内无序圆点对的路径权值和之和。
        g[x]+=g[y]+1ll*siz[x]*sum[y];//g[x]表示 x 到 x 的子树内所有圆点的路径权值和之和。
        sum[x]+=sum[y];//sum[x]表示 x 的子树内圆点的个数。
    }
}

int main()
{
    read(n);read(m);tot=n;
    memset(siz,-1,(n+1)<<2);
    for (int i=1,x,y; i<=m; ++i) read(x),read(y),G1.add(x,y);
    for (int i=1; i<=n; ++i)
        if (!dfn[i]) tarjan(i),dfs(i,0),ans+=f[i]<<1;
    write(ans),puts("");
    return 0;
}

转载于:https://www.cnblogs.com/G-hsm/p/11323260.html

代码下载链接: https://pan.quark.cn/s/b80bd6ed2d38 USB Type-C 协议作为USB接口的最新一代标准,致力于提供更高速的数据传输速率、更强的电源传输性能以及更灵活的连接选择。官方技术文档全面解释了该协议的各个细节,为开发者和工程师提供了系统的技术参考。以下列出该协议的一些主要技术要点: 1. **双向连接特性**:Type-C 最突出的优势在于其可逆性设计,用户可以随意正反方向插入接口,从而避免了传统USB接口常见的插接错误问题。 2. **数据传输性能**:Type-C 兼容USB 3.1规范,其最高数据传输速率可达到10 Gbps(SuperSpeed USB 10标准),同时保持对USB 3.0(5 Gbps)和USB 2.0(480 Mbps)的向下兼容性。 3. **电力供应能力**:Type-C 支持USB Power Delivery (PD) 协议,其最大供电功率可达到100W,显著超越了以往的USB接口规格,足以满足笔记本电脑等高功耗设备的使用需求。PD协议通过动态协商电源供需关系,确保设备在安全的前提下高效用电。 4. **BC1.2充电标准**:Type-C 还支持Battery Charging 1.2 (BC1.2) 标准,能够为移动设备提供快速充电服务,最大电流输出可达1.5A或3A,有效提升了充电效率。 5. **EMarker芯片功能**:在Type-C线缆中,E-Marker芯片扮演着核心角色,它负责存储并传递线缆的技术参数,如数据传输速率、最大电压等级和电流容量,从而保证设备与线缆之间的精准通信。 6. **连接器结构及引脚配置**:Type-C连接器包含24个引脚,涵盖电源线路、数据...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值