杂题小记(2023.03.01)

杂题小记(2023.03.01)

更好的阅读体验戳此进入

[ARC084D] Small Multiple

题面

给定 $ k $ 求一个 $ k $ 的正整数倍数 $ S $ 使得 $ S $ 的数位和最小,输出数位和。

Solution

依然考虑类似同余最短路的思路,每个点表示模 $ k $ 余 $ i $,显然有两种操作,即将原数 $ +1 \(,\) i \to (i + 1) \bmod k $,贡献为 $ 1 $。以及将原数 $ \times 10 $,即 $ i \to (i \times 10) \bmod{k} $,贡献为 $ 0 $,建边后从 $ 1 $ 到 $ 0 $ 跑最短路即可,特别地由于本题特殊性亦可使用 01BFS 解决。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template < typename T = int >
inline T read(void);

int K;
bitset < 110000 > vis;
int dis[110000];

void bfs(void){
    memset(dis, 0x3f, sizeof dis);
    deque < int > cur;
    vis[1] = true, dis[1] = 1;
    cur.push_front(1);
    while(!cur.empty()){
        int p = cur.front(); cur.pop_front();
        if(auto val = p * 10 % K; !vis[val]){
            vis[val] = true;
            dis[val] = min(dis[val], dis[p]);
            cur.push_front(val);
        }
        if(auto val = (p + 1) % K; !vis[val]){
            dis[val] = min(dis[val], dis[p] + 1);
            cur.push_back(val);
        }
    }
}

int main(){
    K = read();
    bfs();
    printf("%d\n", dis[0]);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

LG-P2371 [国家集训队]墨墨的等式

题面

给定序列 $ a_n $ 和 $ l, r $,求有多少 $ b \in [l, r] $ 满足 $ \sum_{i = 1}^n a_ix_i = b $ 存在非负整数解。

Solution

经典同余最短路,和跳楼机那题没什么区别,对 $ r $ 和 $ l - 1 $ 分别求然后减一下即可。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;



template < typename T = int >
inline T read(void);

struct Edge{
    Edge* nxt;
    int to;
    ll val;
    OPNEW;
}ed[7100000];
ROPNEW;
Edge* head[510000];

int N;
int P;
ll l, r;
int A[510000];
ll dis[510000];
ll ans(0);
bitset < 510000 > vis;

void Dijkstra(void){
    memset(dis, 0x3f, sizeof dis);
    priority_queue < pair < ll, int >, vector < pair < ll, int > >, greater < pair < ll, int > > > cur;
    dis[0] = 0, cur.push({0, 0});
    while(!cur.empty()){
        int p = cur.top().second; cur.pop();
        if(vis[p])continue;
        vis[p] = true;
        for(auto i = head[p]; i; i = i->nxt)
            if(dis[SON] > dis[p] + i->val)
                dis[SON] = dis[p] + i->val, cur.push({dis[SON], SON});
    }
}

int main(){
    N = read(), l = read < ll >(), r = read < ll >();
    for(int i = 1; i <= N; ++i)A[i] = read();
    P = A[1];
    for(int i = 0; i < P; ++i)
        for(int j = 2; j <= N; ++j)
            head[i] = new Edge{head[i], (i + A[j]) % P, A[j]};
    Dijkstra();
    ll H = r;
    for(int i = 0; i < P; ++i)
        if(H >= dis[i])
            ans += (H - dis[i]) / P + 1;
    H = l - 1;
    for(int i = 0; i < P; ++i)
        if(H >= dis[i])
            ans -= (H - dis[i]) / P + 1;
    printf("%lld\n", ans);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

LG-P2158 [SDOI2008] 仪仗队

题面

作为体育委员,C 君负责这次运动会仪仗队的训练。仪仗队是由学生组成的 \(N \times N\) 的方阵,为了保证队伍在行进中整齐划一,C 君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。

现在,C 君希望你告诉他队伍整齐时能看到的学生人数。

Solution

不难发现答案转化为求所有横纵坐标在 $ [1, n - 1] $ 且互质的点的个数加 $ 2 $,可感性转化为 $ \sum_{i = 1}^{n - 1} \varphi(i) \times 2 - 1 + 2 $,即对称讨论后减去主对角线上重复计算的。我们令 $ n \leftarrow n - 1 $,尝试通过交换求和严谨推导:

\[\begin{aligned} &\sum_{i = 1}^n \sum_{j = 1}^n [\gcd(i, j) = 1] \\ =&\sum_{i = 1}^n \sum_{j = 1}^i [\gcd(i, j) = 1] + \sum_{i = 1}^n \sum_{j = i}^n [\gcd(i, j) = 1] - \sum_{i = 1}^n[\gcd(i, i) = 1] \\ =&\sum_{i = 1}^n \sum_{j = 1}^i [\gcd(i, j) = 1] + \sum_{j = 1}^n \sum_{j = 1}^i [\gcd(i, j) = 1] - \sum_{i = 1}^n[\gcd(i, i) = 1] \\ =&\sum_{i = 1}^{n} \varphi(i) \times 2 - 1 \end{aligned} \]

线性筛欧拉函数即可。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define LIM (41000)

template < typename T = int >
inline T read(void);

int N;
int phi[LIM + 100];
int ans(0);
basic_string < int > Prime;
bitset < LIM + 100 > notPrime;

int main(){phi[1] = 1;
    for(int i = 2; i <= LIM; ++i){
        if(!notPrime[i])Prime += i, phi[i] = i - 1;
        for(auto p : Prime){
            if(i * p > LIM)break;
            phi[i * p] = i % p ? phi[i] * phi[p] : p * phi[i];
            notPrime[i * p] = true;
            if(i % p == 0)break;
        }
    }
    N = read();
    if(N == 1)printf("0\n"), exit(0);
    for(int i = 1; i <= N - 1; ++i)ans += phi[i] * 2;
    printf("%d\n", ans + 1);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

LG-P2568 GCD

题面

给定 $ n $,求满足 $ \gcd(x, y) $ 为质数的有序数对 $ (x, y) $ 有多少种。

Solution

我们令 $ m $ 为值域在 $ [1, n] $ 的质数个数,朴素推导:

\[\begin{aligned} &\sum_{i = 1}^n \sum_{j = 1}^n [\gcd(i, j) \in P] \\ =& \sum_{i = 1}^n \sum_{j = 1}^n \sum_{p} [\gcd(i, j) = p] \\ =& \sum_{p}\sum_{i = 1}^n \sum_{j = 1}^n [\gcd(\dfrac{i}{p}, \dfrac{j}{p}) = 1] \\ =& \sum_{p}\sum_{i = 1}^{\lfloor \frac{n}{p} \rfloor} \sum_{j = 1}^{\lfloor \frac{n}{p} \rfloor} [\gcd(i, j) = 1] \\ \end{aligned} \]

然后问题就转化为了 LG-P2158 [SDOI2008] 仪仗队,预处理一下即可。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define LIM (11000000)

template < typename T = int >
inline T read(void);

int N;
int phi[LIM + 100];
ll ans(0);
ll sum[LIM + 100];
basic_string < int > Prime;
bitset < LIM + 100 > notPrime;

int main(){phi[1] = 1, notPrime[1] = true;
    for(int i = 2; i <= LIM; ++i){
        if(!notPrime[i])Prime += i, phi[i] = i - 1;
        for(auto p : Prime){
            if((ll)i * p > LIM)break;
            phi[i * p] = i % p ? phi[i] * phi[p] : p * phi[i];
            notPrime[i * p] = true;
            if(i % p == 0)break;
        }
    }N = read();
    for(int i = 1; i <= N; ++i)sum[i] = sum[i - 1] + phi[i] * 2;
    for(auto p : Prime){
        if(p > N)break;
        ans += sum[N / p] - 1;
    }
    printf("%lld\n", ans);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

CF600E Lomsat gelral

题面

给定节点染色的树,对于每个节点询问其子树出现次数最多的颜色,有多个则输出颜色编号和。

Solution

dot 模板题,注意更新最大颜色时的一些细节即可。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;



template < typename T = int >
inline T read(void);

int N;
struct Edge{
    Edge* nxt;
    int to;
    OPNEW;
}ed[210000];
ROPNEW;
Edge* head[110000];
int col[110000];
int siz[110000], hson[110000];
int cnt[110000];
ll ans[110000];
int mxp(0);
ll mxv(0);
queue < int > trash;

void dfs_pre(int p = 1, int fa = 0){
    siz[p] = 1;
    for(auto i = head[p]; i; i = i->nxt){
        if(SON == fa)continue;
        dfs_pre(SON, p);
        siz[p] += siz[SON];
        if(siz[SON] > siz[hson[p]])hson[p] = SON;
    }
}
void Insert(int p){
    ++cnt[col[p]], trash.push(col[p]);
    if(mxp == col[p] || cnt[col[p]] > cnt[mxp])mxp = col[p], mxv = col[p];
    else if(cnt[col[p]] == cnt[mxp])mxv += col[p];
}
void InsertSubt(int p, int fa){
    Insert(p);
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa)InsertSubt(SON, p);
}
void RemoveAll(void){
    while(!trash.empty())--cnt[trash.front()], trash.pop();
    mxp = mxv = 0;
}
void dfs_make(int p = 1, int fa = 0){
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa && SON != hson[p])
            dfs_make(SON, p), RemoveAll();
    if(hson[p])dfs_make(hson[p], p);
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa && SON != hson[p])
            InsertSubt(SON, p);
    Insert(p), ans[p] = mxv;
}

int main(){
    N = read();
    for(int i = 1; i <= N; ++i)col[i] = read();
    for(int i = 1; i <= N - 1; ++i){
        int s = read(), t = read();
        head[s] = new Edge{head[s], t};
        head[t] = new Edge{head[t], s};
    }dfs_pre(), dfs_make();
    for(int i = 1; i <= N; ++i)printf("%lld%c", ans[i], i == N ? '\n' : ' ');
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

LG-P4777 【模板】扩展中国剩余定理(EXCRT)

题面

同余式:

\[\left\{ \begin{array}{c} x \equiv a_1 \pmod{m_1} \\ x \equiv a_2 \pmod{m_2} \\ \vdots \\ x \equiv a_n \pmod{m_n} \end{array} \right. \]

但是不满足 $ m_1, m_2, \cdots, m_n $ 之间两两互质。

求 $ x $ 的最小非负整数解。

Solution

虽然是 exCRT 但是除了问题相似之外与 CRT 似乎没什么关系。

首先考虑存在两个同余方程:

\[\left\{ \begin{array}{c} x \equiv a_1 \pmod{m_1} \\ x \equiv a_2 \pmod{m_2} \end{array} \right. \]

考虑对于线性同余方程的常规转化:

\[\left\{ \begin{array}{c} x = a_1 + b_1m_1 \\ x = a_2 + b_2m_2 \end{array} \right. \]

有:$ a_1 + b_1m_1 = a_2 + b_2m_2 $,整理得到 $ b_1m_1 + (-b_2)m_2 = a_2 - a_1 $。

可以用 exgcd 求得 $ b_1m_1 + (-b_2)m_2 = \gcd(m_1, m_2) $ 的解 $ b_1', b_2' $,同时有 $ d = \dfrac{a_2 - a_1}{\gcd(m_1, m_2)} $,于是有:

\[(db_1')m_1 + (db_2')m_2 = a_2 - a_1 \]

所以现在我们以 $ x = a_1 + b_1m_1 $ 为例,我们显然要最小化 $ b_1 $,于是我们取方程中 $ b_1' $ 的最小非负解,或者说实际上取得应为 $ db_1’ $ 的最小非负解,即 $ b_1 = db_1' $。

Tips:读到这里不难发现,若不满足 $ \gcd(m_1, m_2) \mid a_2 - a_1 $,则方程无解。

于是此时我们可以根据 $ b $ 得出一个满足上述两个同余方程的特解 $ x' $,且可以证明对于上述同余方程的所有解 $ x $,有 $ x = x' + k \operatorname{lcm}(m_1, m_2) $,即:

\[x \equiv x' \pmod{\operatorname{lcm}(m_1, m_2) } \]

此时两个方程被合成为了同一个,直到最终仅剩一个方程时其解即为答案。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template < typename T = int >
inline T read(void);

void exgcd(__int128_t a, __int128_t b, __int128_t &x, __int128_t &y){
    if(!b)return x = 1, y = 0, void();
    exgcd(b, a % b, x, y);
    __int128_t tmp(x);
    x = y;
    y = tmp - a / b * y;
}
void Query(__int128_t a, __int128_t b, __int128_t &x, __int128_t &y, __int128_t c){
    exgcd(a, b, x, y);
    __int128_t d = c / __gcd(a, b);
    x *= d, y *= d;
    __int128_t val = b / __gcd(a, b);
    x = (x % val + val) % val;
    y = (c - a * x) - b;
}

int N;
__int128_t rem1(-1), mod1(-1);

int main(){
    N = read();
    while(N--){
        __int128_t mod2 = read < ll >(), rem2 = read < ll >();
        if(!~rem1 || !~mod1){rem1 = rem2, mod1 = mod2; continue;}
        __int128_t b1, b2;
        Query(mod1, -mod2, b1, b2, rem2 - rem1);
        rem1 = rem1 + b1 * mod1;
        mod1 = mod1 * mod2 / __gcd(mod1, mod2);
        rem1 = (rem1 % mod1 + mod1) % mod1;
    }printf("%lld\n", rem1);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

题面

给定树,边权为 $ a $ 到 $ v $ 之间的字符,对于每个子树求最长路径满足所有边上的字符可以在重新排列后组成一个回文串。

Solution

属于是 dot 里难度较高的题了。

最开始以为求的是子树所有边的答案,光速糊了一个上去然后第三个点就挂了,又看了一遍题才发现这东西求的是路径。。

该说不说这个题意看着总有点像点分治

首先考虑回文串的性质,不难发现只要满足对于所有种类的字符,最多有一种字符数量为奇数则可以构成回文串,且发现共 $ 22 $ 个字符,不难想到对其进行状压,用一个 $ 2^{22} - 1 $ 大小的状态表示每一位对应的字符是否为奇数(注意需要边权下放到点权),同时不难发现仅有 $ 0 $ 或 $ 2^k $ 的状态是合法的。

而对于两个状态的合并,也不难发现满足异或的性质。

于是我们考虑记 $ status_p $ 表示从 $ 1 $ 到 $ p $ 的前缀异或,这样当我们合并两条路径,也就是 $ status_x \oplus status_y $ 的时候,从根节点到其 LCA 的路径会因异或的性质被抵消,这也就是快速维护可以成立的关键之处。

发现共需要讨论三种情况,子树内的路径,一个端点在子树根的路径,两个端点都在子树内但经过子树根的路径。

对于后两种的维护,考虑记录 $ mxdep_s $ 表示 $ s $ 状态下的当前存在的边中的最大深度,这样贪心显然是正确的。以此按照一般 dot 套路跑一下即可,细节较多,还是很有启发性的一道题的。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define INF (0x3f3f3f3f)

template < typename T = int >
inline T read(void);

int N;
struct Edge{
    Edge* nxt;
    int to;
    OPNEW;
}ed[1100000];
ROPNEW;
Edge* head[510000];
int dep[510000], siz[510000], hson[510000];
int ans[510000];
int mxdep[4500000], status[510000];

void dfs_pre(int p = 1, int fa = 0){
    siz[p] = 1, dep[p] = dep[fa] + 1, status[p] ^= status[fa];
    for(auto i = head[p]; i; i = i->nxt){
        if(SON == fa)continue;
        dfs_pre(SON, p);
        siz[p] += siz[SON];
        if(siz[SON] > siz[hson[p]])hson[p] = SON;
    }
}
void ClearAll(int p, int fa){
    mxdep[status[p]] = -INF;
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa)ClearAll(SON, p);
}
void Update(int p, int fa, int rt){
    ans[rt] = max(ans[rt], dep[p] + mxdep[status[p]]);
    for(int i = 0; i <= 21; ++i)ans[rt] = max(ans[rt], dep[p] + mxdep[status[p] ^ (1 << i)]);
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa)Update(SON, p, rt);
}
void Insert(int p, int fa){
    mxdep[status[p]] = max(mxdep[status[p]], dep[p]);
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa)Insert(SON, p);
}
void dfs_make(int p = 1, int fa = 0){
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa && SON != hson[p])dfs_make(SON, p), ClearAll(SON, p);
    if(hson[p])dfs_make(hson[p], p);
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa && SON != hson[p])Update(SON, p, p), Insert(SON, p);
    mxdep[status[p]] = max(mxdep[status[p]], dep[p]);
    ans[p] = max(ans[p], dep[p] + mxdep[status[p]]);
    for(int i = 0; i <= 21; ++i)
        ans[p] = max(ans[p], dep[p] + mxdep[status[p] ^ (1 << i)]);
    ans[p] -= dep[p] << 1;
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa)ans[p] = max(ans[p], ans[SON]);
}

int main(){
    for(int i = 0; i < 4500000; ++i)mxdep[i] = -INF;
    N = read();
    for(int i = 2; i <= N; ++i){
        int s = read(), t = i;
        char c = getchar(); while(!islower(c))c = getchar();
        status[t] = 1 << (c - 'a');
        head[s] = new Edge{head[s], t};
        head[t] = new Edge{head[t], s};
    }dfs_pre(), dfs_make();

    for(int i = 1; i <= N; ++i)printf("%d%c", ans[i], i == N ? '\n' : ' ');
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

CF1009F Dominant Indices

题面

给定一棵以 \(1\) 为根,\(n\) 个节点的树。设 \(d(u,x)\)\(u\) 子树中到 \(u\) 距离为 \(x\) 的节点数。

对于每个点,求一个最小的 \(k\),使得 \(d(u,k)\) 最大。

Solution

很板子的 dot,注意需要一个类似 CF741D 的思路,记录 $ dep $ 后在答案里减去根的 $ dep $。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;



template < typename T = int >
inline T read(void);

int N;
struct Edge{
    Edge* nxt;
    int to;
    OPNEW;
}ed[2100000];
ROPNEW;
Edge* head[1100000];
int siz[1100000], hson[1100000], dep[1100000];
int cnt[1100000];
ll ans[1100000];
int mxp(0);
queue < int > trash;

void dfs_pre(int p = 1, int fa = 0){
    siz[p] = 1, dep[p] = dep[fa] + 1;
    for(auto i = head[p]; i; i = i->nxt){
        if(SON == fa)continue;
        dfs_pre(SON, p);
        siz[p] += siz[SON];
        if(siz[SON] > siz[hson[p]])hson[p] = SON;
    }
}
void Insert(int p){
    ++cnt[dep[p]], trash.push(dep[p]);
    if(cnt[dep[p]] > cnt[mxp])mxp = dep[p];
    else if(cnt[dep[p]] == cnt[mxp])mxp = min(mxp, dep[p]);
}
void InsertSubt(int p, int fa){
    Insert(p);
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa)InsertSubt(SON, p);
}
void RemoveAll(void){
    while(!trash.empty())--cnt[trash.front()], trash.pop();
    mxp = 0;
}
void dfs_make(int p = 1, int fa = 0){
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa && SON != hson[p])
            dfs_make(SON, p), RemoveAll();
    if(hson[p])dfs_make(hson[p], p);
    for(auto i = head[p]; i; i = i->nxt)
        if(SON != fa && SON != hson[p])
            InsertSubt(SON, p);
    Insert(p), ans[p] = mxp - dep[p];
}

int main(){
    N = read();
    for(int i = 1; i <= N - 1; ++i){
        int s = read(), t = read();
        head[s] = new Edge{head[s], t};
        head[t] = new Edge{head[t], s};
    }dfs_pre(), dfs_make();
    for(int i = 1; i <= N; ++i)printf("%lld\n", ans[i]);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

UPD

update-2023_03_01 初稿

posted @ 2023-03-05 12:49  Tsawke  阅读(20)  评论(0编辑  收藏  举报