树上问题练习

树的dfs

std::vector<int> tree[100002];
int father[1000002];
int distance[1000002];
int max_distance[1000002];
bool visited[1000002] = {false};

void dfs(int cur_v, int depth) {
    visited[cur_v] = true;
    max_distance[cur_v] = distance[cur_v] = depth;

    for (auto &i : tree[cur_v]) {
        if (!visited[i]) {
            father[i] = cur_v;
            dfs(i, depth + 1);
            max_distance[cur_v] = std::max(max_distance[cur_v], max_distance[i]);
        }
    }
}
father[n] = 0;
dfs(n, 0);

数据结构练习

线段树练习

区间更新(加法)、区间查询(最值)线段树

//
// Created by jiang on 2020/8/12.
// 银川区域赛G

#include <cstdio>
#include <cstring>
#include <array>

long long seq[100008];
std::array<int, 4> seg_tree[400008]; // 本题build初始化
std::array<int, 4> multi_tags[400008];

/**
 * 下推懒惰标记
 * @param l 左区间端点
 * @param r 右区间端点
 * @param num 序号
 */
void push_down(int l, int r, int num) {
    for (int i = 0; i < 4; ++i)
        if (multi_tags[num][i]) {
            seg_tree[num << 1][i] += multi_tags[num][i];
            seg_tree[num << 1 | 1][i] += multi_tags[num][i];
            multi_tags[num << 1][i] += multi_tags[num][i];
            multi_tags[num << 1 | 1][i] += multi_tags[num][i];
            multi_tags[num][i] = 0;
        }
}

/**
 * 区间更新
 * @param update_l 更新区间左端点
 * @param update_r 更新区间右端点
 * @param value 更新的值
 * @param l 当前搜索区间左端点
 * @param r 当前搜索区间右端点
 * @param num 线段树序号
 */
void update(int update_l, int update_r, long long value, int l, int r, int num) {
#define ADD(n, v) (multi_tags[num][n] += v, seg_tree[num][n] += v)
    if (update_l <= l && r <= update_r) {
        if (value == 2) ADD(0, 1);
        else if (value == 3) ADD(1, 1);
        else if (value == 4) ADD(0, 2);
        else if (value == 5) ADD(2, 1);
        else if (value == 6) {
            ADD(0, 1);
            ADD(1, 1);
        } else if (value == 7) ADD(3, 1);
        else if (value == 8) ADD(0, 3);
        else if (value == 9) ADD(1, 2);
        else if (value == 10) {
            ADD(0, 1);
            ADD(2, 1);
        }
        return;
    }

    push_down(l, r, num);

    int mid = (l + r) / 2;
    if (update_l <= mid) {
        update(update_l, update_r, value, l, mid, num << 1);
    }
    if (mid + 1 <= update_r) {
        update(update_l, update_r, value, mid + 1, r, num << 1 | 1);
    }
    for (int i = 0; i < 4; ++i)
        seg_tree[num][i] = std::max(seg_tree[num << 1][i], seg_tree[num << 1 | 1][i]);
}

/**
 * 区间查询
 * @param query_l 查询区间左端点
 * @param query_r 查询区间右端点
 * @param l 当前搜索区间左端点
 * @param r 当前搜索区间右端点
 * @param num 线段树序号
 * @return 区间查询结果(求区间和)
 */
long long query(int query_l, int query_r, int l, int r, int num) {

    if (query_l <= l && r <= query_r) {
        int max_value = 0;
        for (auto &i : seg_tree[num]) {
            max_value = std::max(max_value, i);
        }
        return max_value;
    }

    push_down(l, r, num);

    long long result = 0;
    int mid = (l + r) / 2;
    if (query_l <= mid) {
        result = std::max(result, query(query_l, query_r, l, mid, num << 1));
    }
    if (mid + 1 <= query_r) {
        result = std::max(result, query(query_l, query_r, mid + 1, r, num << 1 | 1));
    }
    return result;
}

int main() {
    int n, q;
    scanf("%d %d", &n, &q);

    memset(seq, 1, sizeof(seq));
    char op[10];
    int n1, n2, n3;
    for (int i = 0; i < q; ++i) {
        scanf("%s %d %d", op, &n1, &n2);
        if (op[1] == 'U') {
            scanf("%d", &n3);
            update(n1, n2, n3, 1, n, 1);
        } else {
            printf("ANSWER %lld\n", query(n1, n2, 1, n, 1));
        }
    }
    return 0;
}

数论练习

素数筛

luogup1835 求【L, R】间的素数个数

//
// Created by jiang on 2020/8/16.
//

#include <cstdio>
#include <bitset>
#include <vector>

std::bitset<46342> is_prime;
std::bitset<1000001> is_comp;
std::vector<long long> primes;

void thieve() {
    is_prime.flip();
    for (int i = 2; i <= 46341; ++i) {
        if (is_prime[i]) {
            primes.push_back(i);
            for (int j = i; j <= 46341; j += i) {
                is_prime[j] = false;
            }
        }
    }
}

int main() {
    thieve();
    long long l, r;
    scanf("%lld %lld", &l, &r);
    if (l == 1) l = 2;
    for (auto &i : primes) {
        if (i * i > r) break;
        for (long long j = std::max(i << 1, (l + i - 1) / i * i); j <= r; j += i)
            is_comp[j - l] = true;
    }
    printf("%llu\n", r - l + 1 - is_comp.count());
    return 0;
}

GCD、LCM

https://www.luogu.com.cn/problem/P1414

从n个数中取出1、2、3、...、n个数,使其gcd最大。

解:求出每个因数出现的次数,并对每个次数记录最大的因数。

#include <cstdio>

int arr[100009];
int prime_count[1000009] = {0};
int answer[1000009] = {0};

inline int max(int a, int b) {
    return a > b ? a : b;
}

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", arr + i);
    }

    for (int i = 0; i < n; ++i) {
        for (int j = 1; j * j <= arr[i]; ++j) {
            if (arr[i] % j == 0) {
                prime_count[j]++;
                answer[prime_count[j]] = max(answer[prime_count[j]], j);
                if (j * j < arr[i]) {
                    prime_count[arr[i] / j]++;
                    answer[prime_count[arr[i] / j]] = max(answer[prime_count[arr[i] / j]], arr[i] / j);
                }
            }
        }
    }

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

    return 0;
}

hdu5656 求n个数所有挑选组合的gcd之和(gcd+dp)

//
// Created by Jiang Yinzuo on 2020/7/17.
//

#include <cstdio>
#include <algorithm>
#include <cstring>

const int MOD = 100000007;
long long dp[1001][1001];
int arr[1001];
int gcd[1001][1001] = {0};

void get_gcd(int n, int m) {
    for (int i = 1; i <= 1000; ++i) {
        for (int j = 1; j <= 1000; ++j) {
            if (!gcd[i][j]) {
                for (int k = 1; k * i <= n && k * j <= m; ++k) {
                    gcd[k * i][k * j] = k;
                }
            }
        }
    }
}

void solve(int n, int max_gcd) {
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= n; ++i) {
        dp[i][arr[i]] = 1;
        for (int j = 1; j <= max_gcd; ++j) {
            dp[i][j] = (dp[i][j] + dp[i - 1][j]) % MOD;
            dp[i][gcd[arr[i]][j]] = (dp[i][gcd[arr[i]][j]] + dp[i - 1][j]) % MOD;
        }
    }

    long long answer = 0;
    for (int i = 1; i <= max_gcd; ++i) {
        answer = (answer + dp[n][i] * i) % MOD;
    }
    printf("%lld\n", answer);
}

int main() {
    get_gcd(1000, 1000);
    int t;
    scanf("%d", &t);
    while (t--) {
        int n;
        scanf("%d", &n);
        int max_gcd = 0;
        for (int i = 1; i <= n; ++i) {
            scanf("%d", arr + i);
            max_gcd = std::max(max_gcd, arr[i]);
        }
        solve(n, max_gcd);
    }
    return 0;
}

cf1366D 对正整数a,找到a的大于1的因数b,c使得$$\gcd(b+c,a)=1$$

解:若$$\gcd(b, c) = g > 1$$,则$$b+c和a$$至少有g这个公因数。故仅当存在$$\gcd(b, c) = 1$$时,问题有解。

$$gcd(b, c) = 1 \iff gcd(b+c, c) = 1 \iff gcd(b+c,b) = 1 \iff \gcd(b+c, cb^k) = 1$$。故找到a的最小质因数$$p$$(提前打表),解为$$b = p^k, c = \frac{a}{p^k}, c\bmod b \neq 0 $$

//
// Created by Jiang Yinzuo on 2020/7/23.
//

#include <vector>
#include <cstdio>

int min_prime[10000007] = {0};

void get_min_prime_factor() {
    for (int i = 2; i <= 3163; ++i) {
        if (min_prime[i] == 0) {
            for (int j = i * 2; j <= 10000000; j += i) {
                if (min_prime[j] == 0)
                    min_prime[j] = i;
            }
        }
    }
}

std::vector<int> b_ans, c_ans;

void solve(int a) {
    if (min_prime[a] == 0) {
        b_ans.push_back(-1);
        c_ans.push_back(-1);
        return;
    }
    int c = a;
    while (c % min_prime[a] == 0) {
        c /= min_prime[a];
    }
    if (c > 1) {
        b_ans.push_back(min_prime[a]);
        c_ans.push_back(c);
    } else {
        b_ans.push_back(-1);
        c_ans.push_back(-1);
    }
}

int main() {
    get_min_prime_factor();
    int n;
    scanf("%d", &n);
    int a;
    for (int i = 0; i < n; ++i) {
        scanf("%d", &a);
        solve(a);
    }
    for (auto &i : b_ans) {
        printf("%d ", i);
    }
    puts("");
    for (auto &i : c_ans) {
        printf("%d ", i);
    }
    puts("");
    return 0;
}

因数个数定理

hdu6069:求 $$ (∑\limits_{i=l}^rd(i^k)) \bmod998244353 \space \space \space (1≤l≤r≤10^{12},r−l≤10^6,1≤k≤10^7). $$ $$d(x)$$表示x的因数个数。

显然,1e6以上的质数最多存在一个。故先打表得到1-1e6之间的质数。枚举1-1e6中的每个质数。根据该质数在【l, r】中的出现的次数更新$$d(i^k)$$,并将i /= 质数。质数^2大于r时停止遍历。

//
// Created by Jiang Yinzuo on 2020/7/17.
//

#include <cstdio>
#include <vector>
#include <cstring>

const int MOD = 998244353;
bool is_prime[1000006];
std::vector<int> primes;

void sieve(int n) {
    memset(is_prime, true, sizeof(is_prime));
    for (int i = 2; i <= n; ++i) {
        if (is_prime[i]) {
            primes.push_back(i);
            for (int j = i + i; j <= n; j += i) {
                is_prime[j] = false;
            }
        }
    }
}

void solve(long long l, long long r, long long k) {
    static long long num[1000006];
    static long long factor_count[1000006];
    for (long long i = l; i <= r; ++i) {
        num[i - l] = i;
        factor_count[i - l] = 1;
    }

    for (auto &prime : primes) {
        if (prime * prime > r) break;
        long long i = (long long) (l / prime) * prime;
        while (i < l) i += prime;
        for (; i <= r; i += prime) {
            long long count = 0;
            while (num[i - l] % prime == 0) {
                num[i - l] /= prime;
                count++;
            }
            factor_count[i - l] = factor_count[i - l] * (count * k + 1) % MOD;
        }
    }

    long long answer = 0;
    for (long long i = l; i <= r; ++i) {
        if (num[i - l] == 1)
            answer = (answer + factor_count[i - l]) % MOD;
        else answer = (answer + factor_count[i - l] * (k + 1) % MOD) % MOD;
    }
    printf("%lld\n", answer);
}

int main() {
    sieve(1000005);
    int t;
    scanf("%d", &t);
    while (t--) {
        long long l, r, k;
        scanf("%lld %lld %lld", &l, &r, &k);
        solve(l, r, k);
    }
    return 0;
}

南京2018 J

//
// Created by jiang on 2020/9/22.
//

#include <cstdio>
#include <vector>
#include <unordered_map>

constexpr int MAX_N = 1000009;

std::vector<long long> primes;

void euler_thieve() {
    static bool not_prime[MAX_N] = {false};
    for (int i = 2; i * i < MAX_N; i++) {
        if (!not_prime[i]) {
            primes.push_back(i);
        }
        for (int j = 0; j < primes.size() && i * primes[j] < MAX_N; ++j) {
            not_prime[i * primes[j]] = true;
            if (i % primes[j] == 0) {
                break;
            }
        }
    }
}

std::unordered_map<int, std::vector<long long>> factor_pos;

void factor(int x, int pos) {
    // c++11能用for-range循环
    for (int i = 0; i < primes.size() && primes[i] * primes[i] <= x; ++i) {
        if (x % primes[i] == 0) {
            factor_pos[primes[i]].push_back(pos);
            do {
                x /= primes[i];
            } while (x % primes[i] == 0);
        }
    }
    if (x > 1) factor_pos[x].push_back(pos);
}

int main() {
    euler_thieve();
    int n, a;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a);
        factor(a, i);
    }
    long long ans = 0;
    for (auto &i : factor_pos) {
        ans += i.second[0] * (n - i.second[0] + 1);
        for (int j = 1; j < i.second.size(); ++j) {
            ans += (i.second[j] - i.second[j - 1]) * (n - i.second[j] + 1);
        }
    }
    printf("%lld\n", ans);
    return 0;
}
CCPC威海2020 D

判断一个数c$$( 1\le c \le 10^{18})$$分解质因数后是否存在相等的质因子。

解:素数筛1e6的质数,c若符合条件,则要么存在两个小于1e6的相等的质因子,要么存在两个大于1e6的相等的质因子、且剩下的质因子都小于1e6。

#include <cstdio>
#include <vector>
#include <cmath>
constexpr int MAX_N = 1000005;
std::vector<long long> primes;
bool not_prime[MAX_N] = {false};

void euler_thieve() {
    for (long long i = 2; i < MAX_N; i++) {
        if (!not_prime[i]) {
            primes.push_back(i);
        }
        for (int j = 0; j < primes.size() && i * primes[j] < MAX_N; ++j) {
            not_prime[i * primes[j]] = true;
            if (i % primes[j] == 0) {
                break;
            }
        }
    }
}
int main() {
    euler_thieve();
    int t;
    scanf("%d", &t);
    while (t--) {
        unsigned long long b;
        scanf("%llu", &b);
        bool yes = false;
        for (auto p : primes) {
            if (b % p == 0) {
                b /= p;
                if (b % p == 0) {
                    yes = true;
                    break;
                }
                if (b == 1) {
                    break;
                }
                auto _temp = (unsigned long long)sqrt(b);
                if (_temp * _temp == b) {
                    yes = true;
                    break;
                }

            }
        }
        auto _temp = (unsigned long long)sqrt(b);
        if (_temp != 1 && _temp * _temp == b) {
            yes = true;
        }
        puts(yes ? "yes" : "no");
    }
    return 0;
}

pollard_rho

//
// Created by jiang on 2020/8/15.
// 徐州ICPC2019 multiply

#include <cstdio>
#include <ctime>
#include <algorithm>
#include <map>

#define TEST_TIMES 8 // 米勒罗宾素性测试次数

std::map<long long, long long> factor_nums;

/**
 * 快速乘法
 * @param a
 * @param b
 * @param p
 * @return
 */
long long mul(long long a, long long b, long long p) {
    long long ans = 0;
    a %= p;
    while (b) {
        if (b & 1)ans = (ans + a) % p;
        b /= 2;
        a = (a + a) % p;
    }
    return ans;
}

/**
 * 快速幂取模
 * @param a
 * @param b
 * @param p
 * @return
 */
long long pow(long long a, long long b, long long p) {
    long long ans = 1;
    a %= p;
    while (b) {
        if (b & 1) ans = mul(a, ans, p);
        b /= 2;
        a = mul(a, a, p);
    }
    ans %= p;
    return ans;
}

/**
 * 米勒罗宾素性测试
 * @param n 测试的大数
 * @param repeat 测试重复次数
 * @return 大概率是素数:true;不是素数:false
 */
bool miller_rabin(long long n, int repeat) {
    if (n == 2 || n == 3)return true;//特判
    if (n % 2 == 0 || n == 1)return false;//偶数和1

    //将n-1分解成2^s*d
    long long d = n - 1;
    int s = 0;
    while (!(d & 1)) ++s, d >>= 1;
    //srand((unsigned)time(NULL));在最开始调用即可
    for (int i = 0; i < repeat; i++)//重复repeat次
    {
        long long a = rand() % (n - 3) + 2;//取一个随机数,[2,n-1)
        long long x = pow(a, d, n);
        long long y = 0;
        for (int j = 0; j < s; j++) {
            y = mul(x, x, n);
            if (y == 1 && x != 1 && x != (n - 1))return false;
            x = y;
        }
        if (y != 1)return false; //费马小定理
    }
    return true;
}

long long gcd(long long a, long long b) {
    return b == 0 ? a : gcd(b, a % b);
}

/**
 * 找到n的一个因子
 * @param n
 * @param c
 * @return
 */
long long pollard_rho(long long n, long long c) {
    long long x = rand() % (n - 2) + 1;
    long long y = x, i = 1, k = 2;
    for (;;) {
        i++;
        x = (mul(x, x, n) + c) + n;//不断调整x2
        long long d = gcd(y - x, n);
        if (1 < d && d < n)
            return d;//找到因子
        if (y == x)
            return n;//找到循环,返回n,重新来
        if (i == k) { //一个优化
            y = x;
            k <<= 1;
        }
    }
}

void find_factor(long long n, long long c) {
    if (n == 1)return;//递归出口

    if (miller_rabin(n, TEST_TIMES)) { //如果是素数,就加入
        factor_nums[n]++;
        return;
    }

    long long p = n;
    while (p >= n)
        p = pollard_rho(p, c--);//不断找因子,知道找到为止,返回n说明没找到

    find_factor(p, c);
    find_factor(n / p, c);
}

long long arr[100005];

/**
 * 计算 n!中质因子 x 的数量
 * @param n
 * @param x
 * @return
 */
long long calc(long long n, long long x) {
    long long num = 0;
    while (n) {
        num += n / x;
        n = n / x;
    }
    return num;
}

void solve(int n, long long x, long long y) {
    factor_nums.clear();
    // c好像也能取2307
    find_factor(x, rand() % (n - 1) + 1);
    long long ans = 1LL << 61;
    for (auto &i : factor_nums) {
        long long z_factor_nums = 0;
        for (int j = 0; j < n; ++j) {
            z_factor_nums += calc(arr[j], i.first);
        }
        long long y_factor_nums = calc(y, i.first);
        ans = std::min(ans, (y_factor_nums - z_factor_nums) / i.second);
    }
    printf("%lld\n", ans);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n;
        long long x, y;
        scanf("%d %lld %lld", &n, &x, &y);
        for (int i = 0; i < n; ++i) {
            scanf("%lld", arr + i);
        }
        solve(n, x, y);
    }
    return 0;
}

数学题练习

银川区域赛F

数学题分界通常到$$\sqrt{n}$$,特别是看到$$n = 10^{12}$$

#include <cstdio>
#include <algorithm>

const long long MOD = 998244353LL;

long long calc(long long a, long long n) {
    long long ans = 0;
    for (long long b = a, t = 1; b <= n; b *= a, ++t) {
        ans = (ans + t * (std::min(b * a, n + 1) - b) % MOD) % MOD;
    }
    return ans;
}

inline long long s1(long long n) {
    n %= MOD;
    return n * (n + 1) / 2 % MOD;
}

inline long long s2(long long n) {
    n %= MOD;
    // 6的逆元
    return (n % MOD * (n + 1) % MOD * (2 * n + 1) % MOD * 166374059) % MOD;
}

int main() {
    long long n;
    scanf("%lld", &n);
    long long ans = 0, a;
    for (a = 2; a * a < n; ++a) {
        ans = (ans + a * calc(a, n) % MOD) % MOD;
    }
    ans = (ans + (n + 1) % MOD * (s1(n) - s1(a - 1) + MOD) % MOD - (s2(n) - s2(a - 1) + MOD) % MOD + MOD) % MOD;

    printf("%lld\n", (ans + MOD) % MOD);
    return 0;
}

图论练习

拓扑排序

POJ1094

#include <cstdio>
#include <queue>
#include <cstring>
#include <vector>

const int MAX_VERTEX_NUM = 26;
static int in_deg[MAX_VERTEX_NUM];
static bool graph[MAX_VERTEX_NUM][MAX_VERTEX_NUM];

const int NOT_SORTED = 2;
const int HAS_CIRCLE = 0;
const int SORTED = 1;

/**
 * 拓扑排序判断一组字符的顺序。维护in_deg数组,记录每个点的入度。
 * 将所有入度为0的点入队,出队时更新以该点为前驱的入度,若有新的点入度为0,则入队。
 * 有环:队列为空后,仍然有点未加入结果向量。
 * 不能确定排序:过程中队列长度大于1
 * 全序:结果向量长度 = 图中点的数量
 */
int topo_sort(int n, std::vector<int> &result) {
    std::queue<int> start_q;
    int temp_in_deg[MAX_VERTEX_NUM];
    for (int i = 0; i < n; ++i) {
        if ((temp_in_deg[i] = in_deg[i]) == 0) start_q.push(i);
    }

    bool not_sorted = false;
    while (!start_q.empty()) {
        if (start_q.size() > 1) not_sorted = true;
        int start = start_q.front();
        result.push_back(start);
        start_q.pop();
        for (int i = 0; i < n; ++i) {
            if (graph[start][i])
                if (--temp_in_deg[i] == 0) start_q.push(i);
        }
    }

    if (result.size() < n) return HAS_CIRCLE;
    return not_sorted ? NOT_SORTED : SORTED;
}

int main() {
    int n, m;
    char rel[4];
    while (scanf("%d %d", &n, &m) && n + m) {
        memset(in_deg, 0, sizeof(in_deg));
        memset(graph, false, sizeof(graph));

        int res;
        bool not_end = true;
        int edge_id = -1;
        std::vector<int> result;
        for (int i = 0; i < m; ++i) {
            scanf("%s", rel);
            if (not_end && !graph[rel[0] - 'A'][rel[2] - 'A']) {
                in_deg[rel[2] - 'A']++;
                graph[rel[0] - 'A'][rel[2] - 'A'] = true;
                result.clear();
                res = topo_sort(n, result);
                if (res == HAS_CIRCLE || res == SORTED) {
                    not_end = false;
                    edge_id = i + 1;
                }
            }
        }
        if (res == HAS_CIRCLE) {
            printf("Inconsistency found after %d relations.\n", edge_id);
        } else if (res == NOT_SORTED) {
            printf("Sorted sequence cannot be determined.\n");
        } else {
            printf("Sorted sequence determined after %d relations: ", edge_id);
            for (int i = 0; i < result.size(); ++i) {
                printf("%c", result[i] + 'A');
            }
            printf(".\n");
        }
    }
    return 0;
}

图的BFS

CCPC2019D Path 求有向图第k短的路径

#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <cstring>

struct Edge {
    int v;
    long long weight;

    bool operator<(const Edge &e) const {
        return weight < e.weight;
    }
};

std::vector<Edge> graph[500009];

struct Path {
    int u, v, idx;
    long long total_weight;

    bool operator>(const struct Path &path) const {
        return total_weight > path.total_weight;
    }
};

static int max_query = 0;
static long long result[500005];

long long solve(int query,  std::priority_queue<Path, std::vector<Path>, std::greater<Path> > &queue) {

    Path p{};
    while (max_query < query && !queue.empty()) {
        p = queue.top();
        queue.pop();
        result[++max_query] = p.total_weight;
        if (0 < p.idx && p.idx < graph[p.u].size()) {
            queue.push({p.u,
                        graph[p.u][p.idx].v,
                        p.idx + 1,
                        p.total_weight - graph[p.u][p.idx - 1].weight + graph[p.u][p.idx].weight});
        }
        if (!graph[p.v].empty()) {
            queue.push({p.v, graph[p.v][0].v, 1, p.total_weight + graph[p.v][0].weight});
        }
    }
    return result[query];
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n, m, q;
        scanf("%d %d %d", &n, &m, &q);

        max_query = 0;
        memset(result, 0, sizeof(result));
        std::priority_queue<Path, std::vector<Path>, std::greater<Path> > queue;
        for (int i = 1; i <= n; ++i) graph[i].clear();

        int u, v;
        long long w;
        for (int i = 0; i < m; ++i) {
            scanf("%d %d %lld", &u, &v, &w);
            graph[u].push_back({v, w});
            queue.push({u, v, -1, w});
        }
        for (int i = 1; i <= n; ++i) std::sort(graph[i].begin(), graph[i].end());

        int query;
        for (int i = 0; i < q; ++i) {
            scanf("%d", &query);
            printf("%lld\n", solve(query, queue));
        }
    }
    return 0;
}