Carpet【2017-2018 ACM-ICPC, NEERC, Moscow Subregional Contest C】【重链剖分 or 思维拓扑+BFS】

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_41730082/article/details/102529450

题目链接 gym 101611 2017-2018 ACM-ICPC, NEERC, Moscow Subregional Contest Problem C


  先讲讲比赛的时候队友想出来的方法(外加我千辛万苦敲出来,错了3发找了3处bug最后封榜后过的)

  很多人都会想到这样的一个问题:如果是一条长链,或者是一个菊花图呢(一个根结点,下面1e5个点)。这是两种不同的类型。


解法一:

  那样,我们举例来做这样的一个问题:

我的做法是什么呢?

我把红色的这些直接链缩成一个点,然后把他们放在同一排。

那么,我们可以缩点成为这样

  那么,就是我们把链给合成成为一个点了,于是乎,我们只要把所有叶子结点放在目前能放的最底下的一排,然后删除所有的叶子结点(删除?此时拓扑不就是最好的了嘛!),然后,我们是不是会形成新的叶子结点,重复上面的操作,就是可以得到一个从底层上来的知道最后的1号结点一张分层图了,这时候,我们对这张分层图进行bfs遍历,就可以确定它的位置了,对了顺序是要固定的,下面是从后往前,那么往上也得是从后往前,不然会有直线相交的。

#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int maxN = 1e5 + 7;
int N, head[maxN], cnt, root[maxN], du[maxN], col[maxN] = {0};
struct Eddge
{
    int nex, to;
    Eddge(int a=-1, int b=0):nex(a), to(b) {}
}edge[maxN<<1];
struct node
{
    int x, y, id;
    node(int a=0, int b=0, int c=0):x(a), y(b), id(c) {}
}t[maxN];
queue<node> que;
inline void addEddge(int u, int v)
{
    edge[cnt] = Eddge(head[u], v);
    head[u] = cnt++;
}
inline void _add(int u, int v) { addEddge(u, v); addEddge(v, u); }
queue<int> Q, P;
vector<int> vt[maxN];
void dfs_fa(int u, int fa) //处理父亲节点出来
{
    root[u] = fa;
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == fa) continue;
        du[u]++;
        dfs_fa(v, u);
    }
}
void tp_sort()
{
    if(Q.empty()) return;
    unordered_set<int> st;
    while(!Q.empty())
    {
        int u = Q.front(); Q.pop();
        if(root[u] == 0) continue;
        int v = root[u];
        if(!v) continue;
        du[v]--;
        while(!du[v] && !st.count(v) && v)
        {
            col[v] = col[u];
            vt[col[u]].push_back(v);
            v = root[v];
            if(v != root[u]) du[v]--;
        }
        if(v == 0) continue;
        if(!du[v])
        {
            P.push(v);
            col[v] = v;
            vt[v].push_back(v);
        }
        st.insert(v);
    }
    while(!Q.empty()) Q.pop();
    while(!P.empty()) { Q.push(P.front()); P.pop(); }
    tp_sort();
}
int max_y = 1, now_x = 1;
void bfs()
{
    while(!que.empty())
    {
        node now = que.front(); que.pop();
        int u = now.id;
        if(now.y > max_y) { now_x = 1; max_y = now.y; }
        for(int i=head[u], v; ~i; i=edge[i].nex)
        {
            v = edge[i].to;
            if(v == root[u]) continue;
            if(t[v].x) continue;
            t[v] = node(now_x++, now.y+1, v);
            que.push(t[v]);
            int len = vt[col[v]].size();
            for(int j=len-1; j>=0; j--)
            {
                int id = vt[col[v]][j];
                if(t[id].x) continue;
                t[id].y = now.y + 1;
                t[id].x = now_x++;
                t[id].id = id;
                if(id ^ v) que.push(t[id]);
            }
        }
    }
}
inline void init()
{
    cnt = 0;
    for(int i=0; i<=N; i++) head[i] = -1;
    while(!Q.empty()) Q.pop();
    while(!P.empty()) P.pop();
}
int main() {
    scanf("%d", &N);
    init();
    for(int i=1, u, v; i<N; i++)
    {
        scanf("%d%d", &u, &v);
        _add(u, v);
    }
    if(N == 1)
    {
        printf("1 1\n");
        return 0;
    }
    dfs_fa(1, 0);
    for(int i=2; i<=N; i++)
    {
        if(!du[i])
        {
            Q.push(i);
            col[i] = i;
            vt[i].push_back(i);
        }
    }
    tp_sort();
    if(!col[1])
    {
        col[1] = 1;
        vt[1].push_back(1);
    }
    int len = vt[col[1]].size();
    for(int i=0, u; i<len; i++)
    {
        u = vt[col[1]][i];
        t[u] = node(i + 1, 1, u);
        que.push(node(i + 1, 1, u));
    }
    now_x = 1;
    max_y = 1;
    bfs();
    for(int i=1; i<=N; i++){
        printf("%d %d\n", t[i].x, t[i].y);
        if(t[i].x > 1000000 || t[i].y > 20)while(1);
    }
    return 0;
}
/*
13
1 2
1 3
1 4
3 5
3 6
3 7
4 8
6 9
6 10
6 11
7 12
8 13
 */

解法二:

  树链剖分!!!

  真的好巧妙,要知道重链剖分中的重儿子和轻儿子,重儿子指的是重链上,如果我们把重链放在一个底上,然后不断的向上叠加轻链,我们就能保证其构成一个多叉树的形式,最后能放的结点的个数一定是大于1e5的。

  对于上面的树,我们按照重链和轻链的方法,会形成这样的一张图:

第一排是y==1时候,第二排是y==2时候,然后x从上往下递增。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&( -x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define efs 1e-7
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e5 + 7;
int N;
vector<int> E[maxN];
inline void _add(int u, int v)
{
    E[u].push_back(v);
    E[v].push_back(u);
}
int siz[maxN], Wson[maxN], father[maxN], top[maxN];
struct node
{
    int x, y;
    node(int a=0, int b=0):x(a), y(b) {}
}t[maxN];
void dfs1(int u, int fa)
{
    siz[u] = 1;
    father[u] = fa;
    int maxx = 0, len = (int)E[u].size();
    for(int i=0, v; i<len; i++)
    {
        v = E[u][i];
        if(v == fa) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(siz[v] > maxx)
        {
            maxx = siz[v];
            Wson[u] = v;
        }
    }
}
void dfs2(int u, int topy)
{
    top[u] = topy;
    if(!Wson[u]) return;
    t[Wson[u]] = node(t[u].x + siz[u] - siz[Wson[u]], t[u].y);
    dfs2(Wson[u], topy);
    int det = 0, len = (int)E[u].size();
    for(int i=0, v; i<len; i++)
    {
        v = E[u][i];
        if(v == father[u] || v == Wson[u]) continue;
        t[v] = node(t[u].x + det, t[u].y + 1);
        dfs2(v, v);
        det += siz[v];
    }
}
inline void init()
{
    for(int i=1; i<=N; i++) Wson[i] = 0;
}
int main()
{
    scanf("%d", &N);
    init();
    for(int i=1, u, v; i<N; i++)
    {
        scanf("%d%d", &u, &v);
        _add(u, v);
    }
    dfs1(1, 0);
    t[1] = node(1, 1);
    dfs2(1, 1);
    for(int i=1; i<=N; i++) printf("%d %d\n", t[i].x, t[i].y);
    return 0;
}
/*
13
1 2
1 3
1 4
2 5
2 6
2 7
3 8
6 9
6 10
8 11
11 12
12 13
*/

猜你喜欢

转载自blog.csdn.net/qq_41730082/article/details/102529450