1. 程式人生 > >Codeforces1076E. Vasya and a Tree(dfs+離線+樹狀陣列維護)

Codeforces1076E. Vasya and a Tree(dfs+離線+樹狀陣列維護)

題目連結:傳送門

題目:

E. Vasya and a Tree
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Vasya has a tree consisting of n
vertices with root in vertex 1. At first all vertices has 0

written on it.

Let d(i,j)
be the distance between vertices i and j, i.e. number of edges 
in the shortest path from i to j. Also, let's denote k-subtree of vertex x — set of vertices y such that next two conditions are met: x is the ancestor of y (each vertex is the ancestor of itself); d(x,y)≤k . Vasya needs you to process m queries. The i-th query is a triple vi, di and xi. For each query Vasya adds value xi to each vertex from
di-subtree of vi . Report to Vasya all values, written on vertices of the tree after processing all queries. Input The first line contains single integer n (1≤n≤3105 ) — number of vertices in the tree. Each of next n−1 lines contains two integers x and y (1≤x,y≤n) — edge between vertices x and y . It
is guarantied that given graph is a tree. Next line contains single integer m (1≤m≤3105 ) — number of queries. Each of next m lines contains three integers vi, di, xi (1≤vi≤n, 0≤di≤109, 1≤xi≤109) — description of the i -th query. Output Print n integers. The i-th integers is the value, written in the i -th vertex after processing all queries. Examples Input Copy 5 1 2 1 3 2 4 2 5 3 1 1 1 2 0 10 4 10 100 Output Copy 1 11 1 100 0 Input Copy 5 2 3 2 1 5 4 3 4 5 2 0 4 3 10 1 1 2 3 2 3 10 1 1 7 Output Copy 10 24 14 11 11 Note In the first exapmle initial values in vertices are 0,0,0,0,0 . After the first query values will be equal to 1,1,1,0,0. After the second query values will be equal to 1,11,1,0,0. After the third query values will be equal to 1,11,1,100,0.
View Code

題目大意:

  給定一棵有N個節點的有向樹,根節點為1。

  有M次操作,對以vi為根的深度為di的子樹上的所有節點權值加xi

  1≤n,m≤3⋅105,1 ≤ vi ≤ n,0 ≤ di ≤ 109,1 ≤ xi ≤ 109

思路:

  離線處理,把每個更新都放到對應的vi上,然後從節點1開始dfs。

  dfs時遇到一個節點後,把這個節點所有的“操作”都拿出來:

  對於每個“操作”,更新當前深度到本次操作影響的最大深度[dep, dep+di]區間內的值加上xi,回溯的時候再減掉xi。這裡可以用樹狀陣列維護。

  每個節點的答案就是被搜尋到之後,“操作”結束之後的當前深度的值。

  到這裡就已經可以AC了。

  但是樹狀陣列的logn的複雜度還可以繼續優化,用一個字首和陣列sum維護,更新[dep, dep+di]區間時,只要讓sum[dep] += xi,sum[dep+di+1] -= xi,就可以實現整個區間的加減了。

  有人問(就是我):“中間的明明沒有加上去啊???”

  “。。。對,但是你是一個個跑過來的啊,把之前的字首加過來不就好了?”(w神)

  period。

程式碼:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAX_NM = 65;

int n, m, t, act;
string opt[10];
string acts[10];
ll F[MAX_NM], A[60][MAX_NM][MAX_NM], AAA[MAX_NM][MAX_NM];

inline int num(int i, int j) {
    return (i-1)*m + j;
}

void mul(ll f[MAX_NM], ll a[MAX_NM][MAX_NM]) {
    ll c[MAX_NM];
    memset(c, 0, sizeof c);
    for (int j = 0; j < MAX_NM; j++)
        for (int k = 0; k < MAX_NM; k++)
            c[j] += f[k] * a[k][j];
    memcpy(f, c, sizeof c);
}

void mulb(ll a[MAX_NM][MAX_NM], ll b[MAX_NM][MAX_NM]) {
    ll c[MAX_NM][MAX_NM];
    memset(c, 0, sizeof c);
    for (int i = 0; i < MAX_NM; i++)
        for (int j = 0; j < MAX_NM; j++)
            for (int k = 0; k < MAX_NM; k++)
                c[i][j] += a[i][k]*b[k][j];
    memcpy(a, c, sizeof c);
}

void mulself(ll a[MAX_NM][MAX_NM]) {
    ll c[MAX_NM][MAX_NM];
    memset(c, 0, sizeof c);
    for (int i = 0; i < MAX_NM; i++)
        for (int j = 0; j < MAX_NM; j++)
            for (int k = 0; k < MAX_NM; k++)
                c[i][j] += a[i][k]*a[k][j];
    memcpy(a, c, sizeof c);
}

void init()
{
    memset(A, 0, sizeof A);
    memset(F, 0, sizeof F);
    F[0] = 1;
    for (int k = 0; k < 60; k++) {
        A[k][0][0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                int index = opt[i-1][j-1] - '0';
                int indey = k % acts[index].size();
                char ch = acts[index][indey];

                if (isupper(ch)) {
                    switch (ch) {
                        case 'N':
                            if (i-1 >= 1)
                                A[k][num(i, j)][num(i-1, j)] = 1; break;
                        case 'S':
                            if (i+1 <= n)
                                A[k][num(i, j)][num(i+1, j)] = 1; break;
                        case 'W':
                            if (j-1 >= 1)
                                A[k][num(i, j)][num(i, j-1)] = 1; break;
                        case 'E':
                            if (j+1 <= m)
                                A[k][num(i, j)][num(i, j+1)] = 1; break;
                        case 'D':
                            A[k][num(i, j)][num(i, j)] = 0;
                    }
                }
                if (isdigit(ch)) {
                    A[k][num(i, j)][num(i, j)] = 1;
                    A[k][0][num(i, j)] = ch - '0';
                }


            }
        }
    }
    for (int i = 0; i < MAX_NM; i++)
        AAA[i][i] = 1;
    for (int k = 0; k < 60; k++)
        mulb(AAA, A[k]);
}

int main()
{
    cin >> n >> m >> t >> act;
    for (int i = 0; i < n; i++)
        cin >> opt[i];
    for (int i = 0; i < act; i++)
        cin >> acts[i];
    init();
    int q = t/60;
    int r = t%60;
    // t = q*60 + r;
    for (; q; q >>= 1) {
        if (q&1)
            mul(F, AAA);
        mulself(AAA);
    }
    for (int i = 0; i < r; i++)
        mul(F, A[i]);
    ll ans = 0;
    for (int i = 1; i < MAX_NM; i++)
        ans = max(ans, F[i]);
    cout << ans << endl;
    return 0;
}
View Code

參考部落格:

  wyboooo's blog