CF980F 题解

37 篇文章 0 订阅
32 篇文章 0 订阅

CF980F

每一个点都指属于一个环,考虑对于每一个环进行计算答案。

考虑基环树的情况,我们会计算出其余的点,之后对于环上的点单独进行 D p Dp Dp

这边也是同理,找到每一个点属于的环,钦定一下每一个环的根。

因为计算距离需要使用换根 D p Dp Dp

我们考虑需要预处理写什么,也就是对于不同环之间的转移,然后对于同一个环内我们需要记录环根的答案。

对于不同环之间的转移,对于每一个点设 f 0 i f0_i f0i 表示向下,从所属环不同的点到当前环的最小距离。

然后对于环根还要特别记录一下 f 1 i f1_i f1i 表示考虑环上的情况。

因为环根才可以将之前的节点都考虑到。

之后设 f i = max ⁡ ( f 0 i , f 1 i ) f_i = \max(f0_i, f1_i) fi=max(f0i,f1i) 也就是向下走的答案。

之后考虑向上走,设 g i g_i gi 表示向上走的答案。

我们这个时候可以将 f 0 i = max ⁡ ( f 0 i , g i ) f0_i = \max(f0_i, g_i) f0i=max(f0i,gi) 方便处理。

可以从所属环不同的点进行转移,记录最大最次大值。

之后考虑整个环对于当前点进行转移,也就是 min ⁡ ( l , r ) \min(l, r) min(l,r) 左右两边,我们分别对其进行单调队列即可。


可能因为我代码实现问题,我的数组需要多开大几倍。

#include <bits/stdc++.h>
using namespace std;

//#define Fread
//#define Getmod

#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#define getchar gc
#endif // Fread

template <typename T>
void r1(T &x) {
	x = 0;
	char c(getchar());
	int f(1);
	for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
	x *= f;
}

#ifdef Getmod
const int mod  = 1e9 + 7;
template <int mod>
struct typemod {
    int z;
    typemod(int a = 0) : z(a) {}
    inline int inc(int a,int b) const {return a += b - mod, a + ((a >> 31) & mod);}
    inline int dec(int a,int b) const {return a -= b, a + ((a >> 31) & mod);}
    inline int mul(int a,int b) const {return 1ll * a * b % mod;}
    typemod<mod> operator + (const typemod<mod> &x) const {return typemod(inc(z, x.z));}
    typemod<mod> operator - (const typemod<mod> &x) const {return typemod(dec(z, x.z));}
    typemod<mod> operator * (const typemod<mod> &x) const {return typemod(mul(z, x.z));}
    typemod<mod>& operator += (const typemod<mod> &x) {*this = *this + x; return *this;}
    typemod<mod>& operator -= (const typemod<mod> &x) {*this = *this - x; return *this;}
    typemod<mod>& operator *= (const typemod<mod> &x) {*this = *this * x; return *this;}
    int operator == (const typemod<mod> &x) const {return x.z == z;}
    int operator != (const typemod<mod> &x) const {return x.z != z;}
};
typedef typemod<mod> Tm;
#endif

template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
    r1(t);  r1(args...);
}

//#define int long long
const int maxn = 2e6 + 5;
const int maxm = maxn << 1;

vector<int> vc[maxn];
void add(int u,int v) {
    vc[u].push_back(v);
}
int f[maxn], g[maxn], f0[maxn], f1[maxn];
int mx[maxn][2];
vector<int> cir[maxn];
int bel[maxn];
int n, m;


int vis[maxn], fa[maxn];

void dfs(int p,int pre) {
    vis[p] = 1, fa[p] = pre;
    for(auto v : vc[p]) {
        if(v == pre) continue;
        if(vis[v]) {
            if(bel[p]) continue;
            ++ m;
            for(int j = p; j != v; j = fa[j])
                bel[j] = m, cir[m].push_back(j);
            bel[v] = m, cir[m].push_back(v);
//            printf("m = %d\n", m);
            continue;
        }
        dfs(v, p);
    }
    if(!bel[p]) {
//            printf("m = %d\n", m);
        cir[++ m].push_back(p);
        bel[p] = m;
    }
}

void dpf(int p) {
    vis[p] = 1;
    for(auto v : vc[p]) {
        if(vis[v]) continue;
        dpf(v);
        if(bel[v] != bel[p]) {
            f0[p] = max(f0[p], f[v] + 1);
            if(!mx[p][0] || f[mx[p][0]] < f[v]) mx[p][1] = mx[p][0], mx[p][0] = v;
            else if(!mx[p][1] || f[mx[p][1]] < f[v]) mx[p][1] = v;
        }
    }
    int x = bel[p], sz = cir[x].size();
    if(p == cir[x][sz - 1]) {
        for(int i = 0; i < sz; ++ i) {
            int v = cir[x][i];
            f1[p] = max(f1[p], min(sz - i - 1, i + 1) + f[v]);
        }
    }// 这个是考虑向下的答案,每一个环的最后一个节点是有用的,之后其余的节点需要进行换根处理
    f[p] = max(f0[p], f1[p]);
}

struct Node {
    int val, id;
    Node(int a = 0,int b = 0) : val(a), id(b) {}
    int operator < (const Node &z) const {
        return val < z.val;
    }
    int operator <= (const Node &z) const {
        return val <= z.val;
    }
}q[maxn];

int hd, tl;

void dpg(int p) {
    vis[p] = 1; int pre = fa[p];
    int x = bel[p], sz = cir[x].size();
    if(pre && bel[pre] != bel[p]) {
        g[p] = max(g[p], g[pre] + 1);
        g[p] = max(g[p], f1[pre] + 1);
        if(mx[pre][0] == p) {
            if(mx[pre][1]) g[p] = max(g[p], f[mx[pre][1]] + 2);
        }
        else g[p] = max(g[p], f0[pre] + 1);
    }
    f0[p] = max(f0[p], g[p]);
    if(p == cir[x][sz - 1]) {
        hd = tl = 1;
        q[1] = Node(f0[cir[x][0]], 0);
        for(int i = 1; i <= sz / 2; ++ i) {
            while(hd <= tl && q[tl] <= f0[cir[x][i]] + i) -- tl;
            q[++ tl] = Node (f0[cir[x][i]] + i, i);
        }
        auto Max = [&] (int &s, const int &c) {
            return (s < c ? s = c : 0), void();
        };
        for(int i = 0; i < sz - 1; ++ i) { // 环根已经被计算过了
            if(q[hd].id == i) ++ hd;
            Max(g[cir[x][i]], q[hd].val - i);
            int nx = i + sz / 2 + 1;
            while(hd <= tl && q[tl] <= f0[cir[x][nx % sz]] + nx) -- tl;
            q[++ tl] = Node (f0[cir[x][nx % sz]] + nx, nx);
        }

        hd = tl = 1;
        q[1] = Node (f0[cir[x][sz / 2]] + sz / 2, sz / 2);
        for(int i = sz / 2 + 1; i < sz; ++ i) {
            while(hd <= tl && q[tl] <= f0[cir[x][i]] + sz - i) -- tl;
            q[++ tl] = Node (f0[cir[x][i]] + sz - i, i);
        }
        for(int i = 0; i < sz - 1; ++ i) {
            if(q[hd].id == sz / 2 + i) ++ hd;
            Max(g[cir[x][i]], q[hd].val + i);
            int nx = sz + i;
            while(hd <= tl && q[tl] <= f0[cir[x][nx % sz]] + sz - nx) -- tl;
            q[++ tl] = Node (f0[cir[x][nx % sz]] + sz - nx, nx);
        }
    }
    for(auto v : vc[p]) if(!vis[v]) dpg(v);
}

signed main() {
//    freopen("S.in", "r", stdin);
//    freopen("S.out", "w", stdout);
    int i, j;
    r1(n, m);
    for(i = 1; i <= m; ++ i) {
        int u, v;
        r1(u, v);
        add(u, v), add(v, u);
    }
    m = 0;
    dfs(1, 0);
    memset(vis, 0, sizeof(vis));
    dpf(1);
    memset(vis, 0, sizeof(vis));
    dpg(1);
    for(i = 1; i <= n; ++ i) printf("%d%c", max(f[i], g[i]), " \n"[i == n]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值