[AGC002D]Stamp Rally-整体二分

Stamp Rally

Problem Statement

We have an undirected graph with N vertices and M edges. The vertices are numbered 1 through N, and the edges are numbered 1 through M. Edge i connects vertices ai and bi. The graph is connected.

On this graph, Q pairs of brothers are participating in an activity called Stamp Rally. The Stamp Rally for the i-th pair will be as follows:

One brother starts from vertex xi, and the other starts from vertex yi.
The two explore the graph along the edges to visit zi vertices in total, including the starting vertices. Here, a vertex is counted only once, even if it is visited multiple times, or visited by both brothers.
The score is defined as the largest index of the edges traversed by either of them. Their objective is to minimize this value.
Find the minimum possible score for each pair.

Constraints

3≤N≤105
N−1≤M≤105
1≤ai

Input

The input is given from Standard Input in the following format:

N M
a1 b1
a2 b2
:
aM bM
Q
x1 y1 z1
x2 y2 z2
:
xQ yQ zQ

Output

Print Q lines. The i-th line should contain the minimum possible score for the i-th pair of brothers.

Sample Input 1

5 6
2 3
4 5
1 2
1 3
1 4
1 5
6
2 4 3
2 4 4
2 4 5
1 3 3
1 3 4
1 3 5

Sample Output 1

1
2
3
1
5
5

尼玛都写完了才知道这是整体二分……
算是学到了新姿势??


题意:
给出一张无向图,每次询问两个点x,y,求以x、y为起点,总共经过z个点,所需经过的编号最大的边的编号最小是多少。

思路:
考虑暴力。
那这就是维护一个并查集,二分加入并查集的边的最大编号,然后直接查询x、y的size是否大于z。
但这样显然过不了。

那么考虑把构造如下三元组(由三个元素构成的struct):
维护一个左区间端点,右区间端点,和答案确定在当前左区间和右区间的所有询问的编号。
考虑维护一个队列,每次从队首取出一个三元组,将它从中间分开成两个区间,同时按照答案归属的区间将询问编号分别压入左半区间和右半区间,并最后将左半区间和右半区间重新加回队列。
这样做到到左右端点重合即可得到属于这个答案区间的询问的答案。

每次取出后,维护一个全局并查集,如果当前的mid小于并查集中最大编号的边,则暴力重构并查集,否则暴力往并查集内加边至 mid,一次性用当前并查集判断当前三元组内询问是否在mid处合法。

这就是传说中的整体二分——一次性对所有询问进行分治。
通过不停地减半缩小区间来保证复杂度,同时维护答案在当前区间内的询问以回答询问。
最大的意义在于它降低了预处理二分的判断依据——在这题就是并查集——的时间,因为直接把相同的一起判断,可以节约很多与处理时间。

脑补出的代码太丑请见谅。

#include
#include
#include
#include
#include
#include
#include

using namespace std;

const int N=1e5+9;

int n,m,k,fa[N],siz[N];
int qx[N],qy[N],qz[N],ans[N];

typedef pair<int,int> pr;
#define u first
#define v second

struct triple
{
    int l,r;
    vector<int> q;

    inline void init(int _l,int _r)
    {
        q.clear();
        l=_l;r=_r;
    }

    triple(int _l=0,int _r=0){init(_l,_r);}
};

queue q;
pr e[N];

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

inline int find(int x)
{
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}

inline void adde(int l,int r)
{
    if(l>r)return;
    for(int i=l,u,v;i<=r;i++)
    {
        u=find(e[i].u),v=find(e[i].v);
        if(u!=v)
        {
            fa[u]=v;
            siz[v]+=siz[u];
            siz[u]=0;
        }
    }
}

inline void init()
{
    for(int i=1;i<=n;i++)
        fa[i]=i,siz[i]=1;
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        e[i].u=read();
        e[i].v=read();
    }

    k=read();
    for(int i=1;i<=k;i++)
    {
        qx[i]=read();
        qy[i]=read();
        qz[i]=read();
    }

    triple tmp(0,m),lp,rp;
    for(int i=1;i<=k;i++)
        tmp.q.push_back(i);
    q.push(tmp);

    int cursize=0;
    init();
    while(!q.empty())
    {
        tmp=q.front();
        q.pop();
        if(tmp.l==tmp.r)
        {
            for(int i=0,ed=tmp.q.size();icontinue;
        }

        int mid=tmp.l+tmp.r>>1;
        lp.init(tmp.l,mid);
        rp.init(mid+1,tmp.r);

        if(mid1,mid);
        else if(mid>cursize)
            adde(cursize+1,mid);
        cursize=mid;

        for(int i=0,ed=tmp.q.size(),cur,cx,cy,cz;iif((cx=find(cx))==(cy=find(cy)))
            {
                if(siz[cx]else
                    lp.q.push_back(cur);
            }
            else
            {
                if(siz[cx]+siz[cy]else
                    lp.q.push_back(cur);
            }
        }
        if(lp.q.size())
            q.push(lp);
        if(rp.q.size())
            q.push(rp);
    }

    for(int i=1;i<=k;i++)
        printf("%d\n",ans[i]);

    return 0;
}

你可能感兴趣的:(整体二分【Holistic,Bisection】)