zoukankan      html  css  js  c++  java
  • 【LOJ2587】铁人两项(APIO2018)-圆方树+树上统计

    测试地址:铁人两项
    做法:本题需要用到圆方树+树上统计。
    首先有个结论:一个点双连通分量内的任意三个点,不管以什么顺序排列,都存在一条简单路径按顺序经过这三个点。
    那么如果我们选定了路径的起点和终点,可以作为中间点的点,就是它们之间的路径所经过的所有点双连通分量中的点(除了两端)。于是我们就把圆方树建出来,方点的点权设为对应点双中的点数,为了去重,圆点的点权设为1,那么可以作为中间点的点数就等于两个点在圆方树路径上的点权和。于是我们很自然地转化成求每个点的贡献,也就是求经过该点的路径数量,这就是NOIP难度的树上统计了,那么我们就完成了这一题,时间复杂度为O(n)
    我傻逼的地方:假圆方树毁一生……经过兜兜转转,总算找到了真正正确的圆方树写法,以前的做法都是假的……我真的是醉了…..
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,first[200010]={0},tot=0,firstt[200010]={0},totpbc;
    int low[200010],dfn[200010],tim=0,st[200010],top=0;
    bool vis[200010]={0};
    ll ans=0,val[200010],sum=0;
    struct edge
    {
        int v,next;
    }e[400010],t[200010];
    
    void insert(int a,int b)
    {
        e[++tot].v=b;
        e[tot].next=first[a];
        first[a]=tot;
    }
    
    void insertt(int a,int b)
    {
        t[++tot].v=b;
        t[tot].next=firstt[a];
        firstt[a]=tot;
    }
    
    void tarjan(int v,int fa)
    {
        sum++;
        val[v]=0-1;
        low[v]=dfn[v]=++tim;
        st[++top]=v;
        vis[v]=1;
        int now=top;
        for(int i=first[v];i;i=e[i].next)
            if (e[i].v!=fa)
            {
                if (!vis[e[i].v])
                {
                    tarjan(e[i].v,v);
                    low[v]=min(low[v],low[e[i].v]);
                    if (low[e[i].v]>=dfn[v])
                    {
                        totpbc++;
                        val[totpbc]=1;
                        insertt(v,totpbc);
                        do
                        {
                            insertt(totpbc,st[top]);
                            val[totpbc]++;
                        }while(st[top--]!=e[i].v);
                    }
                }
                else low[v]=min(low[v],dfn[e[i].v]);
            }
    }
    
    int solve(int v,bool type)
    {
        ll siz=type;
        for(int i=firstt[v];i;i=t[i].next)
        {
            ll now=solve(t[i].v,!type);
            ans+=now*(sum-now)*val[v];
            siz+=now;
        }
        ans+=(sum-siz)*siz*val[v];
        if (type) ans+=(sum-1ll)*val[v];
        return siz;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            insert(a,b),insert(b,a);
        }
    
        tot=0,totpbc=n;
        for(int i=1;i<=n;i++)
            if (!vis[i])
            {
                sum=0;
                tarjan(i,0);
                solve(i,1);
            }
        printf("%lld",ans);
    
        return 0;
    }
  • 相关阅读:
    php $_SERVER中的SERVER_NAME 和HTTP_HOST的区别
    手机web——自适应网页设计(html/css控制)
    js正则表达式语法
    禁止鼠标右键的代码(转)
    php获取文件名称和扩展名
    php中奖概率算法,可用于刮刮卡,大转盘等抽奖算法
    js中cookie的使用详细分析
    fopen中r+和w+的区别
    左右选择框 js插件
    SpringMVC 过滤器Filter使用解析
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793322.html
Copyright © 2011-2022 走看看