2019 ICPC Asia Xuzhou Regional

2019 ICPC Asia Xuzhou Regional

题号 标题 通过率 我的状态
A Cat题库链接 240/893 通过
B Cats line up 题库链接 2/6 未通过
C ❤️ numbers 题库链接 287/803 通过
D Polycut 题库链接 0/8 未通过
E Multiply 题库链接 69/362 通过
F The Answer to the Ultimate Question of Life, The Universe, and Everything. 题库链接 243/801 通过
G Factory 题库链接 0/1 未通过
H Yuuki and a problem 题库链接 22/127 通过
I Interesting game 题库链接 5/20 未通过
J Loli, Yen-Jen, and a graph problem 题库链接 20/83 未通过
K K-rectangle 题库链接 1/5 未通过
L Loli, Yen-Jen, and a cool problem 题库链接 18/90 通过
M M. Kill the tree 题库链接 67/584 通过

A - Cat

计蒜客 - 42540

每四个组成一组,然后左右端点枚举一下即可(代码写的有点搓)

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
int T;
ll l, r, s;
int main() {
    cin >> T;
    while(T--){
        scanf("%lld%lld%lld", &l, &r, &s);
        if(r - l + 1 <= 4){
            ll res = 0, cur = 0;
            for (ll i = l; i<=r;i++){
                cur = 0;
                for (ll j = i; j <= r;j++){
                    cur ^= j;
                    if(cur <= s)
                        res = max(res, j - i + 1);
                }
            }
            printf("%lld
", res ? res : -1);
            continue;
        }
        ll res = 0;
        if(l % 2 == 0){
            ll nr = (r - l + 1) / 4 * 4 + l;
            ll cur = 0;
            for (ll j = nr; j <= r; j++) {
                cur ^= j;
                if(cur <= s)
                    res = max(res, j - nr + 1);
            }
            res += nr - l;
        }
        if(l % 2 == 1){
            ll nl = l + 1;
            ll nr = (r - nl + 1) / 4 * 4 + nl;
            ll cur = 0;
            for (ll j = nr; j <= r;j++){
                cur ^= j;
                if(cur <= s)
                    res = max(res, j - nr + 1);
            }
            cur = l;
            if(cur <= s)
                res = max(res, 1ll);
            for (ll j = nr; j <= r;j++){
                cur ^= j;
                if(cur <= s)
                    res = max(res, j - nr + 2);
            }
            res += nr - nl;
        }
        printf("%lld
", res);
    }
    return 0;
}

C - ❤️ numbers

计蒜客 - 42542

当区间长度特别长时,素数个数是非常少的,参考:https://baike.baidu.com/item/%E7%B4%A0%E6%95%B0%E5%88%86%E5%B8%83/369166

所以大区间直接输出Yes,小区间暴力判断即可。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 100000 + 5;

int T, l, r;
bool prime(int x){
    if(x == 1 || x == 2)
        return true;
    for (int i = 2; i <= x / i;i++){
        if(x % i == 0)
            return false;
    }
    return true;
}
bool check(int l,int r){
    int num = 0;
    for (int i = l; i <= r;i++){
        if(prime(i))
            num++;
    }
    if(3 * num < r - l + 1)
        return true;
    else
        return false;
}
int main() {
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &l, &r);
        if(r - l + 1 >= 50){
            puts("Yes");
        }else{
            puts(check(l, r) ? "Yes" : "No");
        }
    }
    return 0;
}

E - Multiply

计蒜客 - 42544

(Z = a_1! imes a_2! imes cdots imes a_n!)

找最大的 (i) 使得 (Z imes X^i)(Y!) 的因子,首先求出 (X!) 的所有质因数和其指数,然后遍历这些质因数,对于所有(a_i!),以及(Y!),求出其对(X) 的指数的贡献,取所有质因数里面最小的那个即可。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
/*
Miller_Rabin 算法进行素数测试
速度快可以判断一个 < 2^63 的数是不是素数
*/
const int S = 8;
typedef long long ll;
//计算 res = (a*b)%c
ll mult_mod(ll a, ll b, ll c){
    a %= c;
    b %= c;
    ll res = 0;
    ll tmp = a;
    for (; b;b>>=1){
        if(b & 1) {
            res += tmp;
            if(res > c)
                res -= c;
        }
        tmp <<= 1;
        if(tmp > c)
            tmp -= c;
    }
    return res;
}
ll pow_mod(ll a,ll b, ll mod){
    ll res = 1;
    a %= mod;
    for (; b;b>>=1){
        if(b & 1)
            res = mult_mod(res, a, mod);
        a = mult_mod(a, a, mod);
    }
    return res;
}
/*
通过 a^(n-1)=1(mod n)来判断 n 是不是素数
n - 1 = x * 2^t 中间使用二次判断
是合数返回true,不一定是合数返回false
*/
bool check(ll a,ll n,ll x,ll t){
    ll res = pow_mod(a, x, n);
    ll last = res;
    for (int i = 1; i <= t;i++){
        res = mult_mod(res, res, n);
        if(res == 1 && last != 1 && last != n-1)
            return true;
        last = res;
    }
    if(res != 1)
        return true;
    return false;
}
/*
Miller_Rabin 算法
是素数返回true
不是素数返回false
*/
bool Miller_Rabin(ll n){
    if(n < 2)
        return false;
    if(n == 2)
        return true;
    if((n&1) == 0)
        return false;
    ll x = n - 1;
    ll t = 0;
    while((x & 1) == 0) {
        x >>= 1, t++;
    }
    for (int i = 0; i < S;i++){
        ll a = rand() % (n - 1) + 1;
        if(check(a,n,x,t))
            return false;
    }
    return true;
}
/*
Pollard_rho 算法进行质因数分解
*/
ll fac[100];
int tol;
ll gcd(ll a,ll b){
    ll t;
    while(b){
        t = a;
        a = b;
        b = t % b;
    }
    if(a >= 0)
        return a;
    else
        return -a;
}
//找出一个因子
ll pollard_rho(ll x,ll c){
    ll i = 1, k = 2;
    ll x0 = rand() % (x - 1) + 1;
    ll y = x0;
    while(1){
        i++;
        x0 = (mult_mod(x0, x0, x) + c) % x;
        ll d = gcd(y - x0, x);
        if(d != 1 && d != x)
            return d;
        if(y == x0)
            return x;
        if(i == k) {
            y = x0;
            k += k;
        }
    }
}
//对 n 进行质因数分解,存入factor,k设置为107左右
void findfac(ll n,int k){
    if(n == 1)
        return;
    if(Miller_Rabin(n)){
        fac[tol++] = n;
        return;
    }
    ll p = n;
    int c = k;
    while(p >= n)
        p = pollard_rho(p, c--);
    findfac(p, k);
    findfac(n / p, k);
}
//POJ 1811
// 给出一个N,如果是素数输出Prime,否则输出最小的素因子
ll a[100010];
int main(){
    int T;
    ll n,x,y;
    scanf("%d", &T);
    while(T--){
        tol = 0;
        scanf("%lld%lld%lld", &n, &x, &y);
        for (int i = 1; i <= n;i++){
            scanf("%lld", &a[i]);
        }
        ll res = 1ll << 60;
        findfac(x, 127);
        for (int i = 0; i < tol;i++){
            ll now = 0;
            while(x % fac[i] == 0){
                now++;
                x /= fac[i];
            }
            if(now == 0) continue;
            ll nu = 0, tp, w;
            //cout << fac[i] << endl;
            for (int j = 1; j <= n;j++){
                tp = log(a[j]) / log(fac[i]);
                w = 1;
                for (int k = 1; k <= tp;k++)
                    w *= fac[i], nu -= a[j] / w;
            }
            w = 1;
            tp = log(y) / log(fac[i]);
            for (int k = 1; k <= tp;k++)
                w *= fac[i], nu += y / w;
            
            res = min(res, nu/now);
        }
        printf("%lld
", res);
    }
}

F - The Answer to the Ultimate Question of Life, The Universe, and Everything.

计蒜客 - 42545

(a^3+b^3+c^3=x) 已知 (y=x^3) 在实数域上严格单调递增,所以枚举一个a,然后枚举一个b,根据确定的x求出c,然后再根据c的值对b进行调整(双指针),可以预处理出来合法的(a^3+b^3+c^3=x) ,复杂度是(O(n*5000^2)) ,打表出来直接输出即可。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 5000 + 5;

struct node{
    int a, b, c;
} a[201];
bool b[201];
void init(){
    a[0] = {5000,0,-5000};
    a[1] = {5000,1,-5000};
    a[2] = {4375,-486,-4373};
    a[3] = {4,4,-5};
    b[4] = 1;
    b[5] = 1;
    a[6] = {644,-205,-637};
    a[7] = {168,44,-169};
    a[8] = {5000,2,-5000};
    a[9] = {217,-52,-216};
    a[10] = {683,-353,-650};
    a[11] = {843,-641,-695};
    a[12] = {10,7,-11};
    b[13] = 1;
    b[14] = 1;
    a[15] = {332,-262,-265};
    a[16] = {4118,-588,-4114};
    a[17] = {2977,2195,-3331};
    a[18] = {1671,-1276,-1373};
    a[19] = {91,47,-95};
    a[20] = {2833,-741,-2816};
    a[21] = {445,-287,-401};
    b[22] = 1;
    b[23] = 1;
    a[24] = {8,8,-10};
    a[25] = {2357,1839,-2683};
    a[26] = {2106,237,-2107};
    a[27] = {5000,3,-5000};
    a[28] = {2269,-249,-2268};
    a[29] = {235,-69,-233};
    b[30] = 1;
    b[31] = 1;
    b[32] = 1;
    b[33] = 1;
    a[34] = {1557,-244,-1555};
    a[35] = {1154,-509,-1120};
    a[36] = {2731,2358,-3223};
    a[37] = {445,-84,-444};
    a[38] = {25,16,-27};
    b[39] = 1;
    b[40] = 1;
    b[41] = 1;
    b[42] = 1;
    a[43] = {837,-307,-823};
    a[44] = {8,-5,-7};
    a[45] = {2025,1709,-2369};
    a[46] = {815,-473,-758};
    a[47] = {139,49,-141};
    a[48] = {3991,-1247,-3950};
    b[49] = 1;
    b[50] = 1;
    a[51] = {659,602,-796};
    b[52] = 1;
    a[53] = {2141,1518,-2370};
    a[54] = {3891,-648,-3885};
    a[55] = {3131,1837,-3329};
    a[56] = {559,505,-672};
    a[57] = {982,361,-998};
    b[58] = 1;
    b[59] = 1;
    a[60] = {1202,-163,-1201};
    a[61] = {845,668,-966};
    a[62] = {2903,-1561,-2744};
    a[63] = {146,102,-161};
    a[64] = {5000,4,-5000};
    a[65] = {903,403,-929};
    a[66] = {4,1,1};
    b[67] = 1;
    b[68] = 1;
    a[69] = {398,134,-403};
    a[70] = {2325,824,-2359};
    a[71] = {443,401,-533};
    a[72] = {434,-104,-432};
    a[73] = {344,-146,-335};
    b[74] = 1;
    b[75] = 1;
    b[76] = 1;
    b[77] = 1;
    a[78] = {2123,-829,-2080};
    a[79] = {711,-196,-706};
    a[80] = {1366,-706,-1300};
    a[81] = {2638,-1719,-2368};
    a[82] = {1188,847,-1317};
    a[83] = {3651,1315,-3707};
    b[84] = 1;
    b[85] = 1;
    b[86] = 1;
    a[87] = {4271,-1972,-4126};
    a[88] = {1686,-1282,-1390};
    a[89] = {2036,1953,-2514};
    a[90] = {1798,365,-1803};
    a[91] = {3992,-2912,-3389};
    a[92] = {4039,861,-4052};
    a[93] = {253,-98,-248};
    b[94] = 1;
    b[95] = 1;
    a[96] = {20,14,-22};
    a[97] = {3200,-991,-3168};
    a[98] = {2391,-1638,-2101};
    a[99] = {984,-622,-893};
    a[100] = {1870,-903,-1797};
    a[101] = {2325,319,-2327};
    a[102] = {229,118,-239};
    b[103] = 1;
    b[104] = 1;
    a[105] = {8,-4,-7};
    a[106] = {2760,-1165,-2689};
    a[107] = {1117,947,-1309};
    a[108] = {1345,-948,-1165};
    a[109] = {2924,853,-2948};
    b[110] = 1;
    a[111] = {4966,-2312,-4793};
    b[112] = 1;
    b[113] = 1;
    b[114] = 1;
    a[115] = {11,8,-12};
    a[116] = {1945,-757,-1906};
    a[117] = {962,-555,-896};
    a[118] = {4327,383,-4328};
    a[119] = {3789,-1673,-3677};
    a[120] = {2725,1219,-2804};
    b[121] = 1;
    b[122] = 1;
    a[123] = {38,-16,-37};
    a[124] = {5,0,-1};
    a[125] = {5000,5,-5000};
    a[126] = {2217,-419,-2212};
    a[127] = {4988,-3881,-4034};
    a[128] = {3997,-726,-3989};
    a[129] = {1801,-1238,-1580};
    b[130] = 1;
    b[131] = 1;
    a[132] = {5,2,-1};
    a[133] = {389,167,-399};
    a[134] = {3203,-1766,-3013};
    a[135] = {1395,-629,-1351};
    a[136] = {946,816,-1116};
    a[137] = {801,-428,-758};
    a[138] = {103,-77,-86};
    b[139] = 1;
    b[140] = 1;
    a[141] = {116,104,-139};
    a[142] = {8,-3,-7};
    b[143] = 1;
    a[144] = {3342,-2552,-2746};
    a[145] = {10,-7,-8};
    a[146] = {376,-263,-327};
    a[147] = {2131,1528,-2366};
    b[148] = 1;
    b[149] = 1;
    a[150] = {317,260,-367};
    a[151] = {447,215,-463};
    a[152] = {741,486,-805};
    a[153] = {3744,-695,-3736};
    a[154] = {2145,-516,-2135};
    a[155] = {3721,-1049,-3693};
    b[156] = 1;
    b[157] = 1;
    b[158] = 1;
    a[159] = {1526,383,-1534};
    a[160] = {3972,-1654,-3874};
    a[161] = {4980,-2476,-4767};
    a[162] = {4180,-1417,-4125};
    a[163] = {4033,-2943,-3423};
    a[164] = {79,-59,-66};
    b[165] = 1;
    b[166] = 1;
    b[167] = 1;
    a[168] = {890,-574,-802};
    a[169] = {1521,-1012,-1354};
    a[170] = {4047,-2149,-3834};
    a[171] = {1178,891,-1328};
    b[172] = 1;
    b[173] = 1;
    a[174] = {349,-170,-335};
    b[175] = 1;
    b[176] = 1;
    a[177] = {1169,-160,-1168};
    a[178] = {15,-10,-13};
    a[179] = {2691,1503,-2839};
    b[180] = 1;
    a[181] = {4861,974,-4874};
    a[182] = {91,-29,-90};
    a[183] = {4876,976,-4889};
    b[184] = 1;
    b[185] = 1;
    a[186] = {5,5,-4};
    a[187] = {2000,-1092,-1885};
    a[188] = {1635,318,-1639};
    a[189] = {1974,-1403,-1702};
    a[190] = {4815,-593,-4812};
    a[191] = {399,-215,-377};
    a[192] = {16,16,-20};
    b[193] = 1;
    b[194] = 1;
    b[195] = 1;
    a[196] = {1112,-579,-1057};
    a[197] = {3026,-1606,-2867};
    a[198] = {3809,-1347,-3752};
    a[199] = {2199,508,-2208};
    a[200] = {2334,-638,-2318};
}
int main() {
    int T, x;
    cin >> T;
    init();
    while(T--){
        cin >> x;
        if(b[x])
            printf("impossible
");
        else
            printf("%d %d %d
", a[x].a, a[x].b, a[x].c);
    }
    return 0;
}

H - Yuuki and a problem

计蒜客 - 42547

问题关键如何快速求一个集合中可以组成的数字集的mex。

首先考虑区间中数字范围在[1,1] 中的和 sum,

若 sum = 0, 则 答案为1, 否则可以组成[1,sum] 中的任意一个数字。

然后考虑区间中数字在[1, sum+1] 中的和 nsum,这表示可以组成[1.nsum] 中的任意一个数字,因为[1,1]就可以组成了[1,sum],如果再多一个数字 x ,那么就可以组成[x,x+sum] ,我们只有在[2, sum+1]中找到这样的x,也就是说nsum != sum, 才能使得 sum+1 可以被组成,否则 sum+1 就是第一个无法组成的数字。

然后我们令sum = nsum,继续查询[1, sum+1] 中数字的和即可。

sum的增长速度应该是超过了斐波那契,所以查询次数很少。待修改的查询区间内[l,r]中的数字和,可以用树状数组维护权值线段树

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 200000 + 5;
int n, m, q, a[N], L[30][30], R[30][30], cl, cr;
inline int lowbit(int x) { return x & -x; }
struct Seg{
    struct node{
        int ls, rs;
        ll sum;
        void init() { ls = rs = sum = 0; }
    } t[N * 100];
    int rt[N], tot;
    ll res;
    int newnode(){
        ++tot;
        t[tot].init();
        return tot;
    }
    void init() { memset(rt, 0, sizeof rt);  tot = 0;}
    void update(int &rt, int l,int r,int pos,int v){
        if(!rt)
            rt = newnode();
        t[rt].sum += v;
        if(l == r)
            return;
        int mid = l + r >> 1;
        if(pos <= mid)
            update(t[rt].ls, l, mid, pos, v);
        else
            update(t[rt].rs, mid + 1, r, pos, v);
    }
    void update(int x, int pos, int v) {
        for (; x <= n; x += lowbit(x)) {
            update(rt[x], 1, m, pos, v); 
        }
    }
    void query(int dep, int l,int r,int ql,int qr){
        if(ql > qr)
            return;
        if(l >= ql && r <= qr){
            for (int i = 1; i <= cl;i++)
                res -= t[L[dep][i]].sum;
            for (int j = 1; j <= cr;j++)
                res += t[R[dep][j]].sum;
            return;
        }
        int mid = l + r >> 1;
        if(ql <= mid){
            for (int i = 1; i <= cl;i++)
                L[dep + 1][i] = t[L[dep][i]].ls;
            for (int i = 1; i <= cr;i++)
                R[dep + 1][i] = t[R[dep][i]].ls;
            query(dep + 1, l, mid, ql, qr);
        }
        if(qr > mid){
            for (int i = 1; i <= cl;i++)
                L[dep + 1][i] = t[L[dep][i]].rs;
            for (int i = 1; i <= cr;i++)
                R[dep + 1][i] = t[R[dep][i]].rs;
            query(dep + 1, mid + 1, r, ql, qr);
        }
    }
} seg;
int main() {
    m = 2e5;
    while(~scanf("%d%d",&n,&q)){
        for (int i = 1; i <= n;i++)
            scanf("%d", &a[i]);
        seg.init();
        for (int i = 1; i <= n;i++){
            seg.update(i, a[i], a[i]);
        }
        int op, x, y;
        while(q--){
            scanf("%d%d%d", &op, &x, &y);
            if(op == 1){
                seg.update(x, a[x], -a[x]);
                a[x] = y;
                seg.update(x, a[x], a[x]);
            } else {
                --x;
                cl = cr = 0;
                for (int i = x; i; i -= lowbit(i)){
                    L[0][++cl] = seg.rt[i];
                }
                for (int i = y; i; i-= lowbit(i)){
                    R[0][++cr] = seg.rt[i];
                }
                ll l = 0, r = 0;
                do{
                    r = l + 1;
                    seg.res = 0;
                    seg.query(0, 1, m, 1, min(1ll * m, r));
                    l = seg.res;
                } while (l >= r);
                printf("%lld
", l + 1);
            }
        }
    }
    return 0;
}

L - Loli, Yen-Jen, and a cool problem

计蒜客 - 42551

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 300000 + 5;
const int P = 26;
int n, q;
char s[N];
int root, tr[N][P], cnt[N], tot, trie_id[N], sam_pos[N];
int add(int u, char ch){
    int c = ch - 'A';
    if(!tr[u][c]) tr[u][c] = ++tot;
    u = tr[u][c];
    cnt[u] ++;
    return u;
}
struct node{
    int link, len, trans[P], endpos;
};
struct SAM{
    node S[2*N];
    int head[2*N], ver[2*N], nxt[2*N];
    int size,tot, fa[2*N][20];
    void add(int x, int y){ver[++tot] = y; nxt[tot] = head[x],head[x] = tot;}
	SAM():size(1){}
	int insert(int p, char ch, int cnt){
		int x = ch - 'A';
		int np = ++size;
		S[np].len = S[p].len + 1;
        S[np].endpos = cnt;
		while(p != 0 && !S[p].trans[x]) S[p].trans[x] = np, p = S[p].link;
		if(p == 0)S[np].link = 1;
		else{
			int q, nq;
			q = S[p].trans[x];
			if(S[q].len == S[p].len + 1) S[np].link = q;
			else{
                nq = ++size;
				S[nq] = S[q]; 
                S[nq].len = S[p].len + 1;
                S[nq].endpos = 0;
				S[np].link = S[q].link = nq;
				while(p != 0 && S[p].trans[x] == q) S[p].trans[x] = nq, p = S[p].link;
			}
		}
		return np;
	}
    void dfs(int x, int f){
        fa[x][0] = f;
        for(int i=head[x];i;i=nxt[i]){
            int y = ver[i];
            dfs(y, x);
            S[x].endpos += S[y].endpos;
        }
    }
    void build(){
        for(int i=2;i<=size;i++){
            if(S[i].link) add(S[i].link, i);
        }
        dfs(1,0);
        for(int j=1;j<20;j++){
            for(int i=1;i<=size;i++)
            fa[i][j] = fa[fa[i][j-1]][j-1];
        }
    }
    int query(int u, int len){
        for(int i=19;i>=0;i--)if(S[fa[u][i]].len >= len) u = fa[u][i];
        return S[u].endpos;
    }
}sam;
void bfs(int u){
    queue<int> q;
    q.push(u);
    sam_pos[u] = 1;
    while(q.size()){
        int x = q.front();q.pop();
        for(int i=0;i<P;i++){
            int y = tr[x][i];
            if(!y)continue;
            q.push(y);
            sam_pos[y] = sam.insert(sam_pos[x], 'A'+i, cnt[y]);
        }
    }
}
int main() {
    scanf("%d%d",&n,&q);
    scanf("%s",s+1);
    root = tot = 1;
    trie_id[1] = add(root, s[1]);
    for(int i=2;i<=n;i++){
        int fa;scanf("%d",&fa);
        trie_id[i] = add(trie_id[fa], s[i]);
    }
    bfs(root);
    sam.build();
    while(q--){
        int x, len;scanf("%d%d",&x, &len);
        printf("%d
", sam.query(sam_pos[trie_id[x]], len));
    }
    return 0;
}
/*
6 3
ABABBA
1 1 3 3 4
2 2
2 1
6 4
*/

M - Kill the tree

计蒜客 - 42552

对于以 x 为根的某棵子树,其重心只可能是 x,或者在 x 的重儿子上,所以只需要将 以x 的重儿子 y 为根的子树的重心向上提就好。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 200000 + 5;

int head[N], ver[N << 1], nxt[N << 1], tot;
int n, f[N], son[N];
ll d[N],sz[N];
vector<int> res[N];
void add(int x,int y){
    ver[++tot] = y, nxt[tot] = head[x], head[x] = tot;
}
void dfs(int x,int fa){
    sz[x] = 1;
    for (int i = head[x]; i;i=nxt[i]){
        int y = ver[i];
        if(y == fa)
            continue;
        f[y] = x;
        dfs(y, x);
        sz[x] += sz[y];
        d[x] += d[y] + sz[y];
        if(sz[y] >= sz[son[x]])
            son[x] = y;
    }
    if(sz[x] == 1 || sz[son[x]] * 2 < sz[x]){//sz[son[x]]*2<sz[x]表示重心是x,因为重心如果从x向下移只会使得更加不平衡
        res[x].push_back(x);
        return;
    }
    for (int j = 0; j < res[son[x]].size();j++){
        int v = res[son[x]][j];
        while (v != x && sz[v] < sz[x] - sz[v])//不断向上提
            v = f[v];
        res[x].push_back(v);
        if (v != x && sz[v] == sz[x] - sz[v])//此时发现有存在两个重心的情况
            res[x].push_back(f[v]);
    }
    sort(res[x].begin(), res[x].end());
    res[x].erase(unique(res[x].begin(), res[x].end()), res[x].end());
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i < n;i++){
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y);
        add(y, x);
    }
    dfs(1, 0);
    for (int i = 1; i <= n;i++){
        if(res[i].size() == 1)
            printf("%d
", res[i][0]);
        else
            printf("%d %d
", res[i][0], res[i][1]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/1625--H/p/12470665.html