师大培训前第二次测试

写在前面:

最长上升子序列,一个模型居然错掉了,本题数据较大需要离散,没有对数据范围引起重视,小于等于边界没有判定清楚,细节不到位只有70分;

第二题是dfs序的应用,之前没听懂。。。:现在补上:

摘自:dfs序

DFS序 参考许昊然《数据结构漫谈》

原文

"所谓DFS序, 就是DFS整棵树依次访问到的结点组成的序列"

"DFS序有一个很强的性质: 一颗子树的所有节点在DFS序内是连续的一段, 利用这个性质我们可以解决很多问题"

基本代码 : 

复制代码
void Dfs(int u, int fa, int dep)
{
    seq[++cnt] = u;
    st[u] = cnt;
    int len = edge[u].size();
    for(int i = 0; i < len; i++) {
        int v = edge[u][i];
        if(v != fa) {
            Dfs(v, u, dep+1);
        }
    }
    seq[++cnt] = u;
    ed[u] = cnt;
}
复制代码

 关于原文中列出的7个经典问题, 分别实现一下

给定一颗树, 和每个节点的权值

1. 对某个节点X权值加上一个数W, 查询某个子树X里所有点权的和

解 :

由于X子树在DFS序中是连续的一段, 只需要维护一个序列,

支持单点修改和区间查询, 用树状数组就能实现

复制代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int MAXN = 1e5+10;

vector<int> edge[MAXN];
int s[2*MAXN];
int seq[2*MAXN];
int st[MAXN];
int ed[MAXN];
int cnt;

int Lowbit(int x)
{
    return x & (-x);
}

void Add(int x, int val, int n)
{
    for(int i = x; i <= n; i += Lowbit(i)) {
        s[i] += val;
    }
}

int Sum(int x)
{
    int res = 0;
    for(int i = x; i > 0; i -= Lowbit(i)) {
        res += s[i];
    }
    return res;
}

void Dfs(int u, int fa)
{
    seq[++cnt] = u;
    st[u] = cnt;
    int len = edge[u].size();
    for(int i = 0; i < len; i++) {
        int v = edge[u][i];
        if(v != fa) {
            Dfs(v, u);
        }
    }
    seq[++cnt] = u;
    ed[u] = cnt;
}

void Init(int n)
{
    for(int i = 0; i <= n; i++) {
        edge[i].clear();
    }
    memset(s, 0, sizeof(s));
}

int main()
{
    int n, op;
    int u, v, w;
    int cmd;

    while(scanf("%d %d", &n, &op) != EOF) {
        Init(n);
        for(int i = 0; i < n-1; i++) {
            scanf("%d %d", &u, &v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        cnt = 0;
        Dfs(1, -1);
        while(op--) {
            scanf("%d", &cmd);
            if(cmd == 0) {
                scanf("%d %d", &u, &w);
                Add(st[u], w, cnt);
            }
            else if(cmd == 1) {
                scanf("%d", &u);
                printf("%d\n", Sum(ed[u]) - Sum(st[u] - 1));
            }
        }
    }

    return 0;
}
复制代码

2. 对节点X到Y的最短路上所有点权都加一个数W, 查询某个点的权值

解 :

这个操作等价于

a. 对X到根节点路径上所有点权加W

b. 对Y到根节点路径上所有点权加W

c. 对LCA(x, y)到根节点路径上所有点权值减W

d. 对LCA(x,y)的父节点 parent(LCA(x, y))到根节点路径上所有权值减W

于是要进行四次这样从一个点到根节点的区间修改

将问题进一步简化, 进行一个点X到根节点的区间修改, 查询其他一点Y时

只有X在Y的子树内, X对Y的值才有贡献且贡献值为W

于是只需要更新四个点, 查询一个点的子树内所有点权的和即可

复制代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int MAXN = 1e5+10;

vector<int> edge[MAXN];
int s[2*MAXN];
int seq[2*MAXN];
int seq1[2*MAXN];
int depth[2*MAXN];
int first[MAXN];
int dp[2*MAXN][25];
int st[MAXN];
int ed[MAXN];
int parent[MAXN];
int cnt, num;

int Lowbit(int x)
{
    return x & (-x);
}

void Add(int x, int val, int n)
{
    if(x <= 0) return;
    for(int i = x; i <= n; i += Lowbit(i)) {
        s[i] += val;
    }
}

int Sum(int x)
{
    int res = 0;
    for(int i = x; i > 0; i -= Lowbit(i)) {
        res += s[i];
    }
    return res;
}

void Dfs(int u, int fa, int dep)
{
    parent[u] = fa;
    seq[++cnt] = u;
    seq1[++num] = u;
    first[u] = num;
    depth[num] = dep;
    st[u] = cnt;
    int len = edge[u].size();
    for(int i = 0; i < len; i++) {
        int v = edge[u][i];
        if(v != fa) {
            Dfs(v, u, dep+1);
            seq1[++num] = u;
            depth[num] = dep;
        }
    }
    seq[++cnt] = u;
    ed[u] = cnt;
}

void RMQ_Init(int n)
{
    for(int i = 1; i <= n; i++) {
        dp[i][0] = i;
    }
    for(int j = 1; (1 << j) <= n; j++) {
        for(int i = 1; i + (1 << j) - 1 <= n; i++) {
            int a = dp[i][j-1], b = dp[i + (1 << (j-1))][j-1];
            dp[i][j] = depth[a] < depth[b] ? a : b;
        }
    }
}

int RMQ_Query(int l, int r)
{
    int k = 0;
    while((1 << (k + 1)) <= r - l + 1) k++;
    int a = dp[l][k], b = dp[r-(1<<k)+1][k];
    return depth[a] < depth[b] ? a : b;
}

int LCA(int u, int v)
{
    int a = first[u], b = first[v];
    if(a > b) a ^= b, b ^= a, a ^= b;
    int res = RMQ_Query(a, b);
    return seq1[res];
}

void Init(int n)
{
    for(int i = 0; i <= n; i++) {
        edge[i].clear();
    }
    memset(s, 0, sizeof(s));
}

int main()
{
    int n, op;
    int u, v, w;
    int cmd;

    while(scanf("%d %d", &n, &op) != EOF) {
        Init(n);
        for(int i = 0; i < n-1; i++) {
            scanf("%d %d", &u, &v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        cnt = 0, num = 0;
        Dfs(1, -1, 0);
        RMQ_Init(num);
        while(op--) {
            scanf("%d", &cmd);
            if(cmd == 0) {
                scanf("%d %d %d", &u, &v, &w);
                int lca = LCA(u, v);
                Add(st[u], w, cnt);
                Add(st[v], w, cnt);
                Add(lca, -w, cnt);
                Add(parent[lca], -w, cnt);
            }
            else if(cmd == 1) {
                scanf("%d", &u);
                printf("%d\n", Sum(ed[u]) - Sum(st[u] - 1));
            }
        }
    }

    return 0;
}
复制代码

3. 对节点X到Y的最短路上所有点权都加一个数W, 查询某个点子树的权值之和

解 :

同问题2中的修改方法, 转化为修改某点到根节点的权值加/减W

当修改某个节点A, 查询另一节点B时

只有A在B的子树内, Y的值会增加 W * (depth[A] - depth[B] + 1) == W * (depth[A] + 1) - W * depth[B]

同样是用树状数组来查询Y的子树, 修改 和 查询方法都要新增一个数组

第一个数组保存 W * (depth[A] + 1), 第二个数组保存 W

每次查询结果为Sum(ed[B]) - Sum(st[B]-1) - (Sum1(ed[B]) - Sum(st[B]-1)) * depth[B]

复制代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int MAXN = 1e5+10;

vector<int> edge[MAXN];
int s[2*MAXN];
int s1[2*MAXN];
int seq[2*MAXN];
int seq1[2*MAXN];
int depth[2*MAXN];
int first[MAXN];
int dp[2*MAXN][25];
int st[MAXN];
int ed[MAXN];
int parent[MAXN];
int cnt, num;

int Lowbit(int x)
{
    return x & (-x);
}

void Add(int x, int val, int n)
{
    if(x <= 0) return;
    for(int i = x; i <= n; i += Lowbit(i)) {
        s[i] += val;
    }
}

void Add1(int x, int val, int n)
{
    if(x <= 0) return;
    for(int i = x; i <= n; i += Lowbit(i)) {
        s1[i] += val;
    }
}

int Sum(int x)
{
    int res = 0;
    for(int i = x; i > 0; i -= Lowbit(i)) {
        res += s[i];
    }
    return res;
}

int Sum1(int x)
{
    int res = 0;
    for(int i = x; i > 0; i -= Lowbit(i)) {
        res += s1[i];
    }
    return res;
}

void Dfs(int u, int fa, int dep)
{
    parent[u] = fa;
    seq[++cnt] = u;
    seq1[++num] = u;
    first[u] = num;
    depth[num] = dep;
    st[u] = cnt;
    int len = edge[u].size();
    for(int i = 0; i < len; i++) {
        int v = edge[u][i];
        if(v != fa) {
            Dfs(v, u, dep+1);
            seq1[++num] = u;
            depth[num] = dep;
        }
    }
    seq[++cnt] = u;
    ed[u] = cnt;
}

void RMQ_Init(int n)
{
    for(int i = 1; i <= n; i++) {
        dp[i][0] = i;
    }
    for(int j = 1; (1 << j) <= n; j++) {
        for(int i = 1; i + (1 << j) - 1 <= n; i++) {
            int a = dp[i][j-1], b = dp[i + (1 << (j-1))][j-1];
            dp[i][j] = depth[a] < depth[b] ? a : b;
        }
    }
}

int RMQ_Query(int l, int r)
{
    int k = 0;
    while((1 << (k + 1)) <= r - l + 1) k++;
    int a = dp[l][k], b = dp[r-(1<<k)+1][k];
    return depth[a] < depth[b] ? a : b;
}

int LCA(int u, int v)
{
    int a = first[u], b = first[v];
    if(a > b) a ^= b, b ^= a, a ^= b;
    int res = RMQ_Query(a, b);
    return seq1[res];
}

void Init(int n)
{
    for(int i = 0; i <= n; i++) {
        edge[i].clear();
    }
    memset(s, 0, sizeof(s));
}

int main()
{
    int n, op;
    int u, v, w;
    int cmd;

    while(scanf("%d %d", &n, &op) != EOF) {
        Init(n);
        for(int i = 0; i < n-1; i++) {
            scanf("%d %d", &u, &v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        cnt = 0, num = 0;
        Dfs(1, -1, 0);
        RMQ_Init(num);
        while(op--) {
            scanf("%d", &cmd);
            if(cmd == 0) {
                scanf("%d %d %d", &u, &v, &w);
                int lca = LCA(u, v);
                Add(st[u], w * depth[first[u]] + w, cnt);
                Add1(st[u], w, cnt);
                Add(st[v], w * depth[first[v]] + w, cnt);
                Add1(st[v], w, cnt);
                Add(lca, -(w * depth[first[lca]] + w), cnt);
                Add1(lca, -w, cnt);
                Add(parent[lca], -(w * depth[first[parent[lca]]] + w), cnt);
                Add1(parent[lca], -w, cnt);
            }
            else if(cmd == 1) {
                scanf("%d", &u);
                printf("%d\n", Sum(ed[u]) - Sum(st[u] - 1) - \
                               depth[first[u]] * (Sum1(ed[u]) - Sum1(st[u] - 1)));
            }
        }
    }

    return 0;
}
复制代码

4. 对某个点X权值加上一个数W, 查询X到Y路径上所有点权之和

解 :

求X到Y路径上所有的点权之和, 和前面X到Y路径上所有点权加一个数相似

这个问题转化为

X到根节点的和 + Y到根节点的和 - LCA(x, y)到根节点的和 - parent(LCA(x,y)) 到根节点的和

于是只要支持单点修改, 区间查询即可

要注意的是修改, 要在该点开始出现位置加W, 在结束位置减W

这样能保证在该节点子树内的能加到这个W, 不在该点子树内的无影响

复制代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int MAXN = 1e5+10;

vector<int> edge[MAXN];
int s[2*MAXN];
int s1[2*MAXN];
int seq[2*MAXN];
int seq1[2*MAXN];
int depth[2*MAXN];
int first[MAXN];
int dp[2*MAXN][25];
int st[MAXN];
int ed[MAXN];
int parent[MAXN];
int cnt, num;

int Lowbit(int x)
{
    return x & (-x);
}

void Add(int x, int val, int n)
{
    if(x <= 0) return;
    for(int i = x; i <= n; i += Lowbit(i)) {
        s[i] += val;
    }
}

int Sum(int x)
{
    int res = 0;
    for(int i = x; i > 0; i -= Lowbit(i)) {
        res += s[i];
    }
    return res;
}

void Dfs(int u, int fa, int dep)
{
    parent[u] = fa;
    seq[++cnt] = u;
    seq1[++num] = u;
    first[u] = num;
    depth[num] = dep;
    st[u] = cnt;
    int len = edge[u].size();
    for(int i = 0; i < len; i++) {
        int v = edge[u][i];
        if(v != fa) {
            Dfs(v, u, dep+1);
            seq1[++num] = u;
            depth[num] = dep;
        }
    }
    seq[++cnt] = u;
    ed[u] = cnt;
}

void RMQ_Init(int n)
{
    for(int i = 1; i <= n; i++) {
        dp[i][0] = i;
    }
    for(int j = 1; (1 << j) <= n; j++) {
        for(int i = 1; i + (1 << j) - 1 <= n; i++) {
            int a = dp[i][j-1], b = dp[i + (1 << (j-1))][j-1];
            dp[i][j] = depth[a] < depth[b] ? a : b;
        }
    }
}

int RMQ_Query(int l, int r)
{
    int k = 0;
    while((1 << (k + 1)) <= r - l + 1) k++;
    int a = dp[l][k], b = dp[r-(1<<k)+1][k];
    return depth[a] < depth[b] ? a : b;
}

int LCA(int u, int v)
{
    int a = first[u], b = first[v];
    if(a > b) a ^= b, b ^= a, a ^= b;
    int res = RMQ_Query(a, b);
    return seq1[res];
}

void Init(int n)
{
    for(int i = 0; i <= n; i++) {
        edge[i].clear();
    }
    memset(s, 0, sizeof(s));
}

int main()
{
    int n, op;
    int u, v, w;
    int cmd;

    while(scanf("%d %d", &n, &op) != EOF) {
        Init(n);
        for(int i = 0; i < n-1; i++) {
            scanf("%d %d", &u, &v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        cnt = 0, num = 0;
        Dfs(1, -1, 0);
        RMQ_Init(num);
        while(op--) {
            scanf("%d", &cmd);
            if(cmd == 0) {
                scanf("%d %d", &u, &w);
                Add(st[u], w, cnt);
                Add(ed[u], -w, cnt);
            }
            else if(cmd == 1) {
                scanf("%d %d", &u, &v);
                int lca = LCA(u, v);
                printf("%d\n", Sum(st[u]) + Sum(st[v]) - Sum(st[lca]) - Sum(st[parent[lca]]));
            }
        }
    }

    return 0;
}
复制代码

5. 对子树X里所有节点加上一个值W, 查询某个点的值

解 : 

对DFS序来说, 子树内所有节点加W, 就是一段区间加W

所以这个问题就是 区间修改, 单点查询

复制代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int MAXN = 1e5+10;

vector<int> edge[MAXN];
int s[2*MAXN];
int s1[2*MAXN];
int seq[2*MAXN];
int seq1[2*MAXN];
int depth[2*MAXN];
int first[MAXN];
int dp[2*MAXN][25];
int st[MAXN];
int ed[MAXN];
int parent[MAXN];
int cnt, num;

int Lowbit(int x)
{
    return x & (-x);
}

void Add(int x, int val, int n)
{
    if(x <= 0) return;
    for(int i = x; i <= n; i += Lowbit(i)) {
        s[i] += val;
    }
}

int Sum(int x)
{
    int res = 0;
    for(int i = x; i > 0; i -= Lowbit(i)) {
        res += s[i];
    }
    return res;
}

void Dfs(int u, int fa)
{
    seq[++cnt] = u;
    st[u] = cnt;
    int len = edge[u].size();
    for(int i = 0; i < len; i++) {
        int v = edge[u][i];
        if(v != fa) {
            Dfs(v, u);
        }
    }
    seq[++cnt] = u;
    ed[u] = cnt;
}

void Init(int n)
{
    for(int i = 0; i <= n; i++) {
        edge[i].clear();
    }
    memset(s, 0, sizeof(s));
}

int main()
{
    int n, op;
    int u, v, w;
    int cmd;

    while(scanf("%d %d", &n, &op) != EOF) {
        Init(n);
        for(int i = 0; i < n-1; i++) {
            scanf("%d %d", &u, &v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        cnt = 0;
        Dfs(1, -1);
        while(op--) {
            scanf("%d", &cmd);
            if(cmd == 0) {
                scanf("%d %d", &u, &w);
                Add(st[u], w, cnt);
                Add(ed[u], -w, cnt);
            }
            else if(cmd == 1) {
                scanf("%d", &u);
                printf("%d\n", Sum(u));
            }
        }
    }

    return 0;
}
复制代码

6.对子树X里所有节点加上一个值W, 查询某个子树的权值和

解 :

子树所有节点加W, 就是某段区间加W, 查询某个子树的权值和, 就是查询某段区间的和

区间修改区间求和, 是线段树的经典问题, 用线段树可以很好解决

复制代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

typedef struct {
    int l, r, sum, add;
} Seg;

const int MAXN = 1e5+10;

Seg T[4*MAXN];
vector<int> edge[MAXN];
int s[2*MAXN];
int seq[2*MAXN];
int st[MAXN];
int ed[MAXN];
int cnt;

void Dfs(int u, int fa)
{
    seq[++cnt] = u;
    st[u] = cnt;
    int len = edge[u].size();
    for(int i = 0; i < len; i++) {
        int v = edge[u][i];
        if(v != fa) {
            Dfs(v, u);
        }
    }
    seq[++cnt] = u;
    ed[u] = cnt;
}

void Build(int l, int r, int k)
{
    T[k].l = l, T[k].r = r, T[k].sum = T[k].add;
    if(l == r) return;
    int mid = (l + r) >> 1;
    Build(l, mid, k<<1);
    Build(mid+1, r, k<<1|1);
}

void PushDown(int k)
{
    if(T[k].add) {
        T[k<<1].sum += (T[k<<1].r - T[k<<1].l + 1) * T[k].add;
        T[k<<1].add += T[k].add;
        T[k<<1|1].sum += (T[k<<1|1].r - T[k<<1|1].l + 1) * T[k].add;
        T[k<<1|1].add += T[k<<1].add;
        T[k].add = 0;
    }
}

void PushUp(int k)
{
    T[k].sum = T[k<<1].sum + T[k<<1|1].sum;
}

void Update(int l, int r, int val, int k)
{
    if(T[k].l == l && T[k].r == r) {
        T[k].sum += (r - l + 1) * val;
        T[k].add += val;
        return ;
    }
    PushDown(k);
    int mid = (T[k].l + T[k].r) >> 1;
    if(r <= mid) Update(l, r, val, k<<1);
    else if(l > mid) Update(l, r, val, k<<1|1);
    else {
        Update(l, mid, val, k<<1);
        Update(mid+1, r, val, k<<1|1);
    }
    PushUp(k);
}

int Query(int l, int r, int k)
{
    if(T[k].l == l && T[k].r == r) {
        return T[k].sum;
    }
    PushDown(k);
    int mid = (T[k].l + T[k].r) >> 1;
    if(r <= mid) return Query(l, r, k<<1);
    else if(l > mid) return Query(l, r, k<<1|1);
    else {
        return Query(l, mid, k<<1) + Query(mid+1, r, k<<1|1);
    }
}

void Init(int n)
{
    for(int i = 0; i <= n; i++) {
        edge[i].clear();
    }
}

int main()
{
    int n, op;
    int u, v, w;
    int cmd;

    while(scanf("%d %d", &n, &op) != EOF) {
        Init(n);
        for(int i = 0; i < n-1; i++) {
            scanf("%d %d", &u, &v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        cnt = 0;
        Dfs(1, -1);
        Build(1, cnt, 1);
        while(op--) {
            scanf("%d", &cmd);
            if(cmd == 0) {
                scanf("%d %d", &u, &w);
                Update(st[u], ed[u], w, 1);
            }
            else if(cmd == 1) {
                scanf("%d", &u);
                printf("%d\n", Query(st[u], ed[u], 1) / 2);
            }
        }
    }

    return 0;
}
复制代码

7. 对节点X的子树所有节点加上一个值W, 查询X到Y的路径上所有点的权值和

解:

同问题4把路径上求和转化为四个点到根节点的和

X到根节点的和 + Y到根节点的和 - LCA(x, y)到根节点的和 - parent(LCA(x,y)) 到根节点的和

修改一点A, 查询某点B到根节点时, 只有B在A的子树内, A对B才有贡献

贡献为W * (depth[B] - depth[A] + 1) == W * (1 - depth[A]) + W * depth[B]

和第三题一样, 用两个树状数组维护 W *(depth[A] + 1), W * depth[B]

同样要注意修改某点时, 在一点开始位置加, 在其结束位置减

复制代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

typedef struct {
    int l, r, sum, add;
} Seg;

const int MAXN = 1e5+10;

Seg T[4*MAXN];
vector<int> edge[MAXN];
int s[2*MAXN];
int s1[2*MAXN];
int seq[2*MAXN];
int seq1[2*MAXN];
int depth[2*MAXN];
int first[MAXN];
int dp[2*MAXN][25];
int parent[MAXN];
int st[MAXN];
int ed[MAXN];
int cnt, cnt1;

int Lowbit(int x)
{
    return x & (-x);
}

void Add(int x, int val, int n)
{
    if(x <= 0) return ;
    for(int i = x; i <= n; i += Lowbit(i)) {
        s[i] += val;
    }
}

void Add1(int x, int val, int n)
{
    if(x <= 0) return ;
    for(int i = x; i <= n; i += Lowbit(i)) {
        s1[i] += val;
    }
}

int Sum(int x)
{
    int res = 0;
    for(int i = x; i > 0; i -= Lowbit(i)) {
        res += s[i];
    }
    return res;
}

int Sum1(int x)
{
    int res = 0;
    for(int i = x; i > 0; i -= Lowbit(i)) {
        res += s1[i];
    }
    return res;
}

void RMQ_Init(int n)
{
    for(int i = 1; i <= n; i++) {
        dp[i][0] = i;
    }
    for(int j = 1; (1 << j) <= n; j++) {
        for(int i = 1; i + (1 << j) - 1 <= n; i++) {
            int a = dp[i][j-1], b = dp[i + (1 << (j-1))][j-1];
            dp[i][j] = depth[a] < depth[b] ? a : b;
        }
    }
}

int RMQ_Query(int l, int r)
{
    int k = 0;
    while((1 << (k + 1)) <= r - l + 1) k++;
    int a = dp[l][k], b = dp[r-(1 << k)+1][k];
    return depth[a] < depth[b] ? a : b;
}

int LCA(int u, int v)
{
    int a = first[u], b = first[v];
    if(a > b) a ^= b, b ^= a, a ^= b;
    int res = RMQ_Query(a, b);
    return seq1[res];
}

void Dfs(int u, int fa, int dep)
{
    seq[++cnt] = u;
    seq1[++cnt1] = u;
    first[u] = cnt1;
    parent[u] = fa;
    depth[cnt1] = dep;
    st[u] = cnt;
    int len = edge[u].size();
    for(int i = 0; i < len; i++) {
        int v = edge[u][i];
        if(v != fa) {
            Dfs(v, u, dep+1);
            seq1[++cnt1] = u;
            depth[cnt1] = dep;
        }
    }
    seq[++cnt] = u;
    ed[u] = cnt;
}

void Init(int n)
{
    for(int i = 0; i <= n; i++) {
        edge[i].clear();
    }
    memset(s, 0, sizeof(s));
    memset(s1, 0, sizeof(s1));
}

void Debug()
{
    int u, v;
    while(1) {
        scanf("%d %d", &u, &v);
        printf("The LCA of %d %d is %d\n", u, v, LCA(u, v));
    }
}

int main()
{
    int n, op;
    int u, v, w;
    int cmd;

    while(scanf("%d %d", &n, &op) != EOF) {
        Init(n);
        for(int i = 0; i < n-1; i++) {
            scanf("%d %d", &u, &v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        cnt = cnt1 = 0;
        Dfs(1, 0, 0);
        RMQ_Init(cnt1);
        while(op--) {
            scanf("%d", &cmd);
            if(cmd == 0) {
                scanf("%d %d", &u, &w);
                Add(st[u], w * (1 - depth[first[u]]), cnt);
                Add(ed[u], -w * (1 - depth[first[u]]), cnt);
                Add1(st[u], w, cnt);
                Add1(ed[u], -w, cnt);
            }
            else if(cmd == 1) {
                scanf("%d %d", &u, &v);
                int lca = LCA(u, v);
                int par = parent[lca];
                int ans = Sum(st[u]);
                ans += depth[first[u]] * Sum1(st[u]);
                ans += Sum(st[v]);
                ans += depth[first[v]] * Sum1(st[v]);
                ans -= Sum(st[lca]);
                ans -= depth[first[lca]] * Sum1(st[lca]);
                ans -= Sum(st[par]);
                ans -= depth[first[par]] * Sum1(st[par]);
                printf("%d\n", ans);
            }
        }
    }

    return 0;
}

第三题,n方算法,常数优化太弱。(根本没有),只有60。。。

总:130/300。好菜啊啊啊,一半的分都没拿到

附上题目

                                                  A

题目描述
给出一个长度为 n 的序列 A,求这个序列的最长上升子序列的长度。
输入格式
第一行一个正整数 n。
第二行 n 个正整数,第 i 个正整数表示 A[i]。
输出格式
一个正整数,即最长上升子序列的长度。
样例输入
5
14532
样例输出
3
数据范围和提示
对于 30%的数据:n<=1e3,A[i]<=1e9
另有 30%的数据:n<=1e5,A[i]<=1e5
对于 100%的数据:n<=1e5,A[i]<=1e9

所有数字均为正整数。

考试代码:没加=,

#include<cstdio>
#include<iostream>
using namespace std;
int n,a[100005];
int ans[100005];
int m=0;
void div(int l,int r,int x)
{
         while(l!=r)
         {
               int mid=(l+r)/2;
               if(ans[mid]=>x)r=mid;//
               else l=mid+1;
         }
         ans[l]=x;
         return ;
}
int main()
{           freopen("A.in","r",stdin);freopen("A.out","w",stdout);
            scanf("%d",&n);
            for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
              //cout<<n<<endl;
            for(int i=1;i<=n;i++)
            {
                 if(a[i]>ans[m])++m,ans[m]=a[i];
                else
                    div(1,m,a[i]);
            }//cout<<a[n]<<endl;
            printf("%d",m);
            fclose(stdin);fclose(stdout);
            return 0;
}


                                                          B                      

题目描述
给出一棵大小为 n 的树,根为 1。点有点权。给出 m 个操作或询问:
1. 操作:节点 p 权值增加 w。
2. 询问:两点(u,v)的路径点权和。
输入格式
第一行一个正整数 n。
第二行 n-1 个正整数,表示节点 2 到节点 n 的父亲节点。
第三行 n 个正整数,表示节点 1 到节点 n 的点权 val。
第三行一个正整数 m。
接下来 m 行,每行三个正整数 opt,x,y,若 opt=1 表示将节点 x 的权值增加 y,若 opt=2 表示
查询节点 x 与节点 y 路径的点权和。
输出格式
输出包括若干行,对于每一个询问,输出点权和。
样例输入
10
111355342
1 2 3 4 5 6 7 8 9 10
5
286
259
137278
297
样例输出
22
22
30
36
数据范围
对于 30%的数据:n<=1e3
另有 20%的数据:opt=1 的情况只有 1 次。
另有 20%的数据:opt=2 的情况只有 1 次。
对于 100%的数据:n<=1e5
所有数字均为正整数,且<=1e9

                                                 C

题目描述
给出一个长度为 n 的排列 A,求这个排列有多少个长度为奇数的子串中位数是 mid。
输入格式
第一行两个正整数 n 与 mid。
第二行 n 个正整数,第 i 个数表示 A[i]。
输出格式
一个正整数,即答案。
样例输入
54
15423
样例输出
3
样例解释
这三个子串分别为[1 5 4][5 4 2][4]
数据范围
对于 30%的数据:n<=2e2
对于 60%的数据:n<=2e3
对于 100%的数据:n<=1e6,mid<=n
所有数字均为正整数。

猜你喜欢

转载自blog.csdn.net/k42946/article/details/81043777