【题解】CF1268E Happy Cactus【仙人掌】

题目链接

题意

今有一棵边仙人掌,第 \(i\) 条边边权为 \(i\)。称点对 \((u,v)\) 是好的当且仅当 \(u\) 可以通过边权单增的路径到达 \(v\)。对于每个 \(u\),统计有多少 \((u,v)\) 是好的。\(n,m\leq 5\times 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<<" ";
}

posted @ 2021-11-05 20:04  破壁人五号  阅读(74)  评论(0编辑  收藏  举报