区间问题----多次区间修改,少次单点询问的差分

《定义》

对于原数列:a1 a2 a3.....ai aj........an-1 an

这个数列的差分为ca[j]=aj-ai

这个数列的前缀和为he[j]=aj+ai+..+a2+a1

 我们可以惊奇的发现

差分的前缀和==原数列

前缀和的差分==原数列

利用这个性质:

 差分还有一个惊人的特性:

  差分可以将将在原数组上的区间修改问题,转化为在差分数组上的两次单点修改问题

  差分还可以将在原数组上的单点查询问题,转化为在差分数组上的前缀和问题

 

 《树上差分》

 

 

 

 

 

 比如这道题:

 

 

 

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cstring>
  4 #include <vector>
  5 using namespace std;
  6 const int N = 100005;
  7 vector<int> tree[N];
  8 // ca[i]表示点i管理的边权的差分;
  9 // ans[i]表示点i管理的边权;
 10 //树上差分,叶子节点开始,根节点为停止
 11 int ca[N], ans[N];
 12 int sizer[N], f[N], dep[N], son[N];
 13 void dfs1(int x, int from, int d)
 14 {
 15     f[x] = from, dep[x] = d, sizer[x] = 1, son[x] = -1;
 16     for (int i = 0; i < tree[x].size(); i++)
 17     {
 18         int child = tree[x][i];
 19         if (child == from)
 20             continue;
 21         dfs1(child, x, d + 1);
 22         sizer[x] += sizer[child];
 23         if (son[x] == -1 || sizer[son[x]] < sizer[child])
 24             son[x] = child;
 25     }
 26 }
 27 int top[N], id[N], rk[N], cnt = 0;
 28 void dfs2(int x, int from, int t)
 29 {
 30     ++cnt;
 31     id[x] = cnt, rk[cnt] = x, top[x] = t;
 32     if (son[x] != -1)
 33         dfs2(son[x], x, t);
 34     for (int i = 0; i < tree[x].size(); i++)
 35     {
 36         int child = tree[x][i];
 37         if (child == from || child == son[x])
 38             continue;
 39         dfs2(child, x, child);
 40     }
 41 }
 42 int lca(int a, int b)
 43 {
 44     while (top[a] != top[b])
 45     {
 46         if (dep[top[a]] < dep[top[b]])
 47             swap(a, b);
 48         a = f[top[a]];
 49     }
 50     if (dep[a] > dep[b])
 51         swap(a, b);
 52     return a;
 53 }
 54 void preSum(int x, int from)
 55 {
 56     ans[x] = ca[x];
 57     for (int i = 0; i < tree[x].size(); i++)
 58     {
 59         int child = tree[x][i];
 60         if (child == from)
 61             continue;
 62         //总之,先到叶子节点
 63         preSum(child, x);
 64         ans[x] += ans[child];
 65         /*  cout << "! "
 66               << "child " << child << " "
 67               << "ans[child] " << ans[child] << " "
 68               << "node:" << x << " "
 69               << "ans[x] " << ans[x] << endl; */
 70     }
 71 }
 72 int main()
 73 {
 74     int n;
 75     scanf("%d", &n);
 76     pair<int, int> side[N];
 77     for (int i = 1; i <= n - 1; i++)
 78     {
 79         int a, b;
 80         scanf("%d%d", &a, &b);
 81         side[i] = {a, b};
 82         tree[a].push_back(b), tree[b].push_back(a);
 83     }
 84     //用树剖求解lca
 85     dfs1(1, -1, 1);
 86     dfs2(1, -1, 1);
 87     int m;
 88     scanf("%d", &m);
 89     while (m--)
 90     {
 91         int x, y;
 92         scanf("%d%d", &x, &y);
 93         if (dep[x] < dep[y])
 94             swap(x, y);
 95         //因为这里改变的权重为1,直接++即可
 96         if (top[x] != top[y])
 97             ca[x]++, ca[y]++, ca[lca(x, y)] -= 2;
 98         else
 99             ca[x]++, ca[y]--;
100     }
101     preSum(1, -1);
102     for (int i = 1; i <= n - 1; i++)
103     {
104         int x = side[i].first, y = side[i].second;
105         if (dep[x] < dep[y])
106             swap(x, y);
107         printf("%d ", ans[x]);
108     }
109     return 0;
110 }

 这里讲一下关于根节点的ans[root]其是一直为的

posted @ 2022-08-25 18:29  次林梦叶  阅读(36)  评论(0)    收藏  举报