zoukankan      html  css  js  c++  java
  • 【题解】CF1268E Happy Cactus【仙人掌】

    题目链接

    题意

    今有一棵边仙人掌,第 (i) 条边边权为 (i)。称点对 ((u,v)) 是好的当且仅当 (u) 可以通过边权单增的路径到达 (v)。对于每个 (u),统计有多少 ((u,v)) 是好的。(n,mleq 5 imes 10^5).

    题解

    这里采用官方题解的推导方式。我们首先转化出一个相对形象的题意:

    • 今有一棵边仙人掌,每个结点有一只老鼠,初始每只老鼠各有一种不同的传染病。之后按照输入倒序枚举每条边,让边两端的老鼠互相传染,问最后每只老鼠有多少种不同的传染病。

    容易注意到一种传染病能传染到一只老鼠当且仅当传染病可以通过边权单减的路径到达老鼠。因此题意转化后与原题相同。

    先考虑树的简单情况,这时一只老鼠显然不可能被一种病重复感染两次。我们直接记 (f_i) 为当前第 (i) 只老鼠的传染病数,按照输入倒序枚举每条边 ((u,v)),则新的 (f') 为:(f'_u=f'_v=f_u+f_v)

    上述做法正确的前提在于两只老鼠互相传染前没有公共的传染病,然而当图中有环时,我们需要减去公共的传染病。具体来说,设 ((u,v)) 作为环上边权最小的边,枚举到它时 (u)(v) 在图上已经连通;如果某条边 (e) 既能通过单减的路径到 (u),也能通过单减的路径到 (v),那么 (u,v) 二鼠就共有 (e) 上传染的所有病,我们应当减去之。

    实现时,我们首先找到所有环,并找到其最小边和最大边;若最大边可以通过单减的路径到最小边的两个端点,我们将其记录下来。接着我们倒序枚举每条边,按上述方式转移,同时记录枚举到这条边时 (f_u+f_v),以便之后减去。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+10;
    int v[N],tot=1;
    vector<int>to[N];
    
    bool vis[N],vvis[N];
    vector<int>cy[N];int cnt=0;
    pair<int,int> ss(int x,int fa){
        vis[x]=1;
        pair<int,int> ret={0,0};
        for(int i:to[x]){
            int v=::v[i];
            if(v==fa)continue;
            if(vvis[i]||vvis[i^1])continue;
            vvis[i]=1;
            if(vis[v]){
                ++cnt;
                cy[cnt].push_back(i);
                ret={v,cnt};
                continue;
            }
            auto t=ss(v,x);
            if(t.second){
                cy[t.second].push_back(i);
                if(x!=t.first)ret=t;
            }
        }
        return ret;
    }
    
    int q[N],f[N],g[N];
    int main(){
        ios::sync_with_stdio(0);
        cin.tie(0);
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=m;i++){
            int x,y;
            cin>>x>>y;
            ++tot;v[tot]=y;to[x].push_back(tot);
            ++tot;v[tot]=x;to[y].push_back(tot);
        }
        ss(1,0);
        if(cnt!=m-n+1)return cnt-(m-n+1);
        for(int i=1;i<=cnt;i++)
            for(int j=0;j<cy[i].size();j++){
                if(v[cy[i][j]^1]!=v[cy[i][(j+1)%cy[i].size()]])
                    return i*100;
            }
        for(int i=1;i<=cnt;i++){
            int mx=max_element(cy[i].begin(),cy[i].end())-cy[i].begin();
            rotate(cy[i].begin(),cy[i].begin()+mx,cy[i].end());
            int mn=min_element(cy[i].begin(),cy[i].end())-cy[i].begin();
            if(is_sorted(cy[i].begin(),cy[i].begin()+mn,greater<int>())
             &&is_sorted(cy[i].begin()+mn,cy[i].end())){
                q[cy[i][mn]]=cy[i][0];
            }
        }
        for(int i=1;i<=n;i++)f[i]=1;
        for(int i=tot;i>=2;i-=2){
            int u=v[i],v=::v[i^1];
            int x=f[u]+f[v];
            f[u]=f[v]=(q[i]+q[i^1]?x-g[q[i]+q[i^1]]:x);
            g[i^1]=g[i]=f[u];
        }
        for(int i=1;i<=n;i++)cout<<f[i]-1<<" ";
    }
    
    
    知识共享许可协议
    若文章内无特别说明,公开文章采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
  • 相关阅读:
    requests+lxml+xpath爬取豆瓣电影
    hisi出的H264码流结构
    单片机复位电路原理介绍
    二极管与、或门,三极管非门电路原理
    Windows Route 路由表命令整理
    理解Windows中的路由表和默认网关
    Windows路由表详解
    linux 路由表设置 之 route 指令详解
    linux中service *** start与直接运行/usr/bin/***的区别
    Linux运行与控制后台进程的方法:nohup, setsid, &, disown, screen
  • 原文地址:https://www.cnblogs.com/wallbreaker5th/p/15515020.html
Copyright © 2011-2022 走看看