AtCoder Grand Contest 002D: Stamp Rally Solution

Keywords: less

One can think of a simple way:
For each query, I dichotomize the answer, and then add all the edges numbered less than the answer and collect them. Finally, I can judge whether the sum of the size of the connected blocks where u and v are located is larger than z (note that if u and v can only be calculated once in a connected block), the complexity is O(q n logn)O(q n logn), which needs to be optimized.
Considering the efficiency bottleneck of this method, the main reason is that when we calculate each query, many times the answer of dichotomy is the same. For such a dichotomy with the same answer, if we consider it separately, we need to build many more times and collect them. Obviously, the efficiency is not high.
So let's consider the whole dichotomy.
At first, all the questions are in the range of 1-n. We divide the answer mid into two parts for all the queries. After building and collecting, all the queries are check ed with this set. Some queries must pass and some fail. In the second round, I will have some answers in the range of 1-mid, others in the range of mid+1-r, and then two kinds of questions. Ask for dichotomous answers, and so on.
This kind of complexity seems explosive, because although the dichotomy process only has log layer, the dichotomy process extends to two times. In theory, the worst way is to dichotomize the answer of the number of O(n)O(n) levels. Each time, it must be established and collected, and the complexity degenerates to O (n 2) O (n 2).
But we noticed that we only need to build and collect one answer at each level, at each stage, because for example, there are several answers in this layer, which are divided into two parts: mid1mid1,mid2mid2. If mid1 < mid2 <... < midn mid1 < mid2 < < mid2 < < midn, then after mid1 mid1 is finished, we can continue to add edges to get mid2 mid2 and search set on the basis of mid1 mid1's union search. By doing this from left to right, we can see that the maximum number of additional edges of each layer and search set is O(m)O(m), and there is a common log layer, so the total complexity is O(mlogn)O(mlogn)O(mlogn)

Here's another O(qn) O(qn) approach
We divide the answer into n
Then we enumerate the answers i n each block, from small to large, and check all the queries belonging to the block. The process of check is the same as that of check i n the dichotomous answer, so the complexity of each block is O(rin) O(rin), because n_i=1ri=q i=1nri=q, so the complexity of the second step is O (q n) O (q n), so the total complexity is O (q n). Qn)O(qn)

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=2e9;
const int magic=348;
const double eps=1e-10;
const double pi=3.14159265;

inline int getint()
{
    char ch;int res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

int n,e,m;
Pair edge[100048];
int ans[100048];

struct node
{
    int x,y;
    int needsz;
    int Left,Right;
    int ind;
    inline void init(int xx) {x=getint();y=getint();needsz=getint();Left=1;Right=e;ind=xx;}
    inline bool operator < (const node &x) const
    {
        return ((Left+Right)>>1)<((x.Left+x.Right)>>1);
    }
}q[100048];

namespace DSU
{
    int pre[100048],rnk[100048];
    inline void init()
    {
        for (int i=1;i<=n+10;i++) pre[i]=i,rnk[i]=1;
    }
    inline int find_anc(int x)
    {
        if (pre[x]!=x) pre[x]=find_anc(pre[x]);
        return pre[x];
    }
    inline void update(int x,int y)
    {
        x=find_anc(x);y=find_anc(y);
        if (x==y) return;
        pre[x]=y;rnk[y]+=rnk[x];
    }
    inline int getsz(int x) {return rnk[find_anc(x)];}
}

int main ()
{
    int i,layer,curedge,totsz;
    n=getint();e=getint();
    for (i=1;i<=e;i++) edge[i].x=getint(),edge[i].y=getint();
    m=getint();
    for (i=1;i<=m;i++) q[i].init(i);
    for (layer=1;layer<=20;layer++)
    {
        sort(q+1,q+m+1);
        DSU::init();curedge=0;
        for (i=1;i<=m;i++)
        {
            int mid=((q[i].Left+q[i].Right)>>1);
            while (curedge<mid)
            {
                curedge++;
                DSU::update(edge[curedge].x,edge[curedge].y);
            }
            if (q[i].Left==q[i].Right) continue;
            totsz=DSU::getsz(q[i].x)+DSU::getsz(q[i].y);
            if (DSU::find_anc(q[i].x)==DSU::find_anc(q[i].y)) totsz>>=1;
            if (totsz>=q[i].needsz) q[i].Right=mid; else q[i].Left=mid+1;
        }
    }
    for (i=1;i<=m;i++) ans[q[i].ind]=q[i].Left;
    for (i=1;i<=m;i++) printf("%d\n",ans[i]);
    return 0;
}

Posted by samsbox on Wed, 19 Dec 2018 19:45:05 -0800