Luogu4381 BZOJ1791[IOI2008]Island

Luogu4381 BZOJ1791[IOI2008]Island

This blog program in Los darkBZOJ Valley and through, but using DFS, can lead to explosion in BZOJ stack. (But BZOJ exploded I have no way measured)

Face questions

Are you ready to visit a park, the park by the \ (N \) islands, local authorities from each island \ (i \) starting to build up a length to the other islands \ (L_i \) of the bridge, but the bridge It is a two-way walk. Meanwhile, the ferry between islands between each pair has a dedicated contacts the two islands. Relative to the boat you may prefer to walk. You want to go through the total length of the bridge as long as possible, but subject to the following restrictions:

  • You can choose their own island to start a tour.
  • Any island can not visit more than once.
  • Regardless of any time, you can from the island currently resides (S \) \ to another has never been to the island \ (D \) . From \ (S \) to \ (D \) has the following method:
    • Walking: only when there is a bridge between the two islands is possible. In this case, the length of the bridge will accumulate the total distance you walk in.
    • Ferry: You can choose this method, only if there is no any combination of bridge and previously used by the ferry can \ (S \) went \ (D \) (When checking whether reachable, you should consider all paths including those after you have visited the island).

Note that you do not have to visit all the islands, may not be able to finish all the bridges.
Please write a program, given \ (N \) bridge as well as their length, according to the rule described above, you can calculate the maximum value and the traveled length of the bridge.

solution

By analysis of the surface of the problem, we found that the figure shows a ring forest trees. ( \ (N-\) points \ (n-\) edges does not guarantee FIG Unicom)
we answer each of them is the maximum length of the tree simple ring path. This path is the diameter of the base ring of the tree.
This problem would be required to be converted to ring diameter trees. By analysis, we found that the diameter of the tree ring there are two cases:

  • Group is located on a tree, the tree ring.
  • Cyclic chain group tree path plus two tree ring between them.

Seeking the first case, \ (the DP \) seek to.
For the second case, we have to operate in chains broken ring.


details:

1. Find Ring
This question is to find the ring can not remember \ (father \) , because the ring will look like this:
This one
If you remember \ (father \) that ran less than a ring.
I used the method of direct labeling ran side to prevent reverse side.
The forward edge of the id given topic are even in the construction side, the reverse side is an odd number, so you do not run the reverse side.

    for (register int i = head[x]; i; i = e[i].nxt)
    {
        if (i & 1)
            continue;
    }

We found that the topic was originally established two-way road this does not traverse the entire map, but we do not need to run rings traverse ah, so the disjoint-set maintenance just fine.

class FIND
{
public:
    int fa[MAXN];
    inline int find(int x)
    {
        return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
    }
    inline void merge(int x, int y)
    {
        x = find(x);
        y = find(y);
        if (x != y)
            fa[y] = x;
    }
    inline void init()
    {
        for (register int i = 1; i <= n; ++i)
            fa[i] = i;
    }
} fd;

if (fd.find(i) == i)
{
    now = i;
    rd[0].push_back(now);
    top = 0;
    v[0] = 0;
    dfs_round(i);
} 

2. The diameter of a tree
diameter DP using the Method of the tree, while maintaining the depth of the tree.

void dp(int x)
{
    v[x] = 1;
    for (register int i = head[x]; i; i = e[i].nxt)
    {
        register int y = e[i].ver;
        if (v[y] || rond[y])
            continue;
        dp(y);
        d[now] = max(d[now], f[x] + f[y] + e[i].edge);
        f[x] = max(f[x], f[y] + e[i].edge);
        l[now] = max(l[now], f[x]);
    }
}

3. The diameter of the base ring of the tree
the first has been determined in the case of a tree, where now calculate plus strand path.
Solving the problem of DP ring ring broken into and replicated into double strand.
Use \ (L [i] \) represented by \ (I \) is the longest chain length of the root of the tree, \ (F [I] \) represents \ (I \) to disconnect the path points on the ring length.
Enumeration right point \ (R & lt \) , when the left end point is \ (L \) answers when \ (ans = \ max (ans , L [r] + L [l] + f [r] - f [l] ) \)
in other words, we want to maintain within the legal range, \ (L [i] - f [i] \) the smallest point.
Enumerate the right point, the use of monotonically increasing monotonically queue to maintain the left point.
See Loop Transportation

  • Head-state is not legitimate, the team
  • The current team is the best left endpoint head element, update the answer
  • If the element is greater than the tail current element, playing the tail
  • The current element into the team
    for (register int i = 1; i <= len; ++i)
        g[i] = g[i + len] = l[s[i]];
    ans = 0;
    for (register int i = 1; i < len * 2; ++i)
    {
        while (q.size() && i - q.front() >= len)
            q.pop_front();
        if (q.size())
        {
            ans = max(ans, f[i] - f[q.front()] + g[i] + g[q.front()]);
        }
        while (q.size() && g[i] - f[i] > g[q.back()] - f[q.back()])
            q.pop_back();
        q.push_back(i);
    }

Roughly like this, there are specific comments in the code.
Further particular note that, since only one communication dfs each block does not traverse the entire map, and there is no need to empty again vis dfs array, this will lead to TLE, but this should be clear after FIG vis array is completely traversed .

Put the code

#include <bits/stdc++.h>
using namespace std;
//快读
namespace fdata
{
inline char nextchar()
{
    static const int BS = 1 << 21;
    static char buf[BS], *st, *ed;
    if (st == ed)
        ed = buf + fread(st = buf, 1, BS, stdin);
    return st == ed ? -1 : *st++;
}
template <typename Y>
inline void poread(Y &ret)
{
    ret = 0;
    char ch;
    while (!isdigit(ch = nextchar()))
        ;
    do
        ret = ret * 10 + ch - '0';
    while (isdigit(ch = nextchar()));
}
#undef nextcar
} // namespace fdata
using fdata::poread;
const int MAXN = 1e6 + 10;
int n;
struct node
{
    int nxt, ver, edge;
} e[MAXN << 1];
int head[MAXN], tot = 1;
inline void add(const int &x, const int &y, const int &z)
{
    e[++tot].ver = y;
    e[tot].edge = z;
    e[tot].nxt = head[x];
    head[x] = tot;
}
//并查集 没错我封装了
class FIND
{
public:
    int fa[MAXN];
    inline int find(int x)
    {
        return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
    }
    inline void merge(int x, int y)
    {
        x = find(x);
        y = find(y);
        if (x != y)
            fa[y] = x;
    }
    inline void init()
    {
        for (register int i = 1; i <= n; ++i)
            fa[i] = i;
    }
} fd;
#define int long long
//双端队列 没错我自己封装的
class QUE
{
private:
    long long q[MAXN << 1];
    int l = 1, r;

public:
    inline bool size()
    {
        return r >= l;
    }
    inline void init()
    {
        l = 1, r = 0;
    }
    inline void push_back(const long long x)
    {
        q[++r] = x;
    }
    inline long long back()
    {
        return q[r];
    }
    inline void pop_back()
    {
        --r;
    }
    inline void pop_front()
    {
        ++l;
    }
    inline long long front()
    {
        return q[l];
    }
} q;
bool v[MAXN << 1];
int sta[MAXN], top;
int rond[MAXN], now;
//使用vector记录环上的点
vector<int> rd[MAXN];
//找环
inline bool dfs_round(int x)
{
    //入栈
    sta[++top] = x;
    v[x] = 1;
    for (register int i = head[x]; i; i = e[i].nxt)
    {
        //判断是不是反向边
        if (i & 1)
            continue;
        register int y = e[i].ver;
        //当前点跑到过  已经跑完了这个环 栈中到当前点点之间的点就是环上的点
        if (v[y])
        {
            register int z = 0;
            do
            {
                z = sta[top--];
                assert(z);
                rd[now].push_back(z);
                rond[z] = now;
            } while (z != y);
            return 1;
        }
        if (dfs_round(y))
            return 1;
    }
    sta[top--] = 0;
    return 0;
}
long long f[MAXN << 1], s[MAXN << 1], d[MAXN], l[MAXN << 1];
long long g[MAXN << 1];
//求树的直径
void dp(int x)
{
    v[x] = 1;
    for (register int i = head[x]; i; i = e[i].nxt)
    {
        register int y = e[i].ver;
        if (v[y] || rond[y])
            continue;
        dp(y);
        d[now] = max(d[now], f[x] + f[y] + e[i].edge);
        f[x] = max(f[x], f[y] + e[i].edge);
        l[now] = max(l[now], f[x]);
    }
}
long long ans;
int len;
//跑环
inline void dfs(int x, int nw)
{
    for (register int i = head[x]; i; i = e[i].nxt)
    {
        register int y = e[i].ver;
        if (v[i] || !rond[y])
            continue;
        v[i] = v[i ^ 1] = 1;
        //边的长度
        f[nw] = e[i].edge;
        //环上的点,顺序记录便于DP
        s[nw] = y;
        dfs(y, nw + 1);
    }
}
/*计算答案*/
inline long long sov(const int &now)
{
    int len = rd[now].size();
    s[1] = rd[now][0];
    dfs(rd[now][0], 2);
    q.init();
    //断环成链并复制
    for (register int i = 2; i <= len; ++i)
        f[i + len + 1 - 1] = f[i];
    //求前缀和,O(1)查询区间和
    for (register int i = 1; i <= len * 2; ++i)
        f[i] = f[i - 1] + f[i];
    //将环上每棵树的深度信息复制
    for (register int i = 1; i <= len; ++i)
        g[i] = g[i + len] = l[s[i]];
    ans = 0;
    //DP求第二种情况
    for (register int i = 1; i < len * 2; ++i)
    {
        while (q.size() && i - q.front() >= len)
            q.pop_front();
        if (q.size())
        {
            ans = max(ans, f[i] - f[q.front()] + g[i] + g[q.front()]);
        }
        while (q.size() && g[i] - f[i] > g[q.back()] - f[q.back()])
            q.pop_back();
        q.push_back(i);
    }
    //第一种情况,直接取max
    for (register vector<int>::iterator it = rd[now].begin(); it != rd[now].end(); ++it)
    {
        ans = max(ans, d[*it]);
    }
    return ans;
}
signed main()
{
    poread(n);
    fd.init();
    for (register int i = 1, y, z; i <= n; ++i)
    {
        //建图 维护连通性
        poread(y), poread(z);
        add(i, y, z);
        add(y, i, z);
        fd.merge(i, y);
    }
    for (register int i = 1; i <= n; ++i)
    {
        //遍历每一个连通块 求环
        if (fd.find(i) == i)
        {
            now = i;
            rd[0].push_back(now);
            top = 0;
            v[0] = 0;
            dfs_round(i);
        }
    }
    //整张图完全遍历 清空vis
    memset(v, 0, sizeof(v));
    //对每一个环上的点为根的树求直径和深度
    for (vector<int>::iterator it = rd[0].begin(); it != rd[0].end(); ++it)
    {
        for (register vector<int>::iterator jt = rd[*it].begin(); jt != rd[*it].end(); ++jt)
        {
            now = *jt;
            dp(*jt);
        }
    }
    memset(v, 0, sizeof(v));
    long long ANs = 0;
    //对每一个连通块求解并统计答案
    for (vector<int>::iterator it = rd[0].begin(); it != rd[0].end(); ++it)
    {
        ANs += sov(*it);
    }
    cout << ANs << endl;
    return 0;
}

Guess you like

Origin www.cnblogs.com/Shiina-Rikka/p/11701492.html