动态点分治,带修的。
显然,如果说单纯使用点分治的做法是会超时的,那么我们只要考虑一下继承就好了,别的好像就没什么了。
分析一下整过过程,我们只是改了仅仅一个点,就需要对全局进行重新统计,显然不好,那么修改过得那个点,它的影响范围就是从那个点一直跑上去的一路上的重心,做到这里其实就方便很多了;
然后呗就算一下贡献,加上减去相应的贡献,就能得出正确答案了,以上;
其实在线的不在线的都是一样的,都是那么点东西,分治一下就完事了,无非是一个单独分治一个点,一个要全部分治一遍;
P6329
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define REP(u) for(int i = h[u], v; i, v = e[i].v; i = e[i].n)
const int N = 2e5 + 111 , INF = 1e9 + 7;
int n, m, tot, ans, rt, sum, minn, cnt;
int val[N], h[N], sz[N], fa[N], dep[N], pos[N], ol[N<<1][21], lg[N<<1];
bool vis[N];
vector <int> c[2][N];
struct edge {
int n, v;
}e[N<<1];
void add(int u,int v) {
e[++tot] = (edge) {h[u], v};
h[u] = tot;
}
void dfs0(int u,int f) {
ol[++cnt][0] = u , pos[u] = cnt;
REP(u) if(v^f) dep[v] = dep[u] + 1, dfs0(v,u), ol[++cnt][0] = u;
}
int get_min(int a,int b) {
return dep[a] < dep[b] ? a : b;
}
void get_ol()
{
for(int i = 1; i <= cnt; ++i) lg[i] = 31 - __builtin_clz(i);
for(int t = 1; 1 << t <= cnt; ++t)
for(int i = 1; i + (1 <<t) <= cnt; ++i)
ol[i][t] = get_min(ol[i][t - 1], ol[i + (1 << (t - 1))][t - 1]);
}
int get_dis(int u,int v) {
if (pos[u] > pos[v] ) swp(u,v);
int uu = pos[u] , vv = pos[v] , len = vv - uu + 1;
int lca = get_min(ol[uu][lg[len]], ol[vv - (1 << lg[len]) +1][lg[len]]);
return dep[u] + dep[v] - 2*dep[lca];
}
#define lowbit(x) (x & -x)
void upd(int u,int opt,int x,int addv) {
x ++;
for(int i=x ; i <= sz[u]; i += lowbit(i) ) c[opt][u][i] += addv;
}
int qry(int u,int opt,int x) {
x ++ ;
int res = 0;
x = min(x, sz[u]);
for(int i =x; i ; i -= lowbit(i)) res += c[opt][u][i];
return res;
}
void find_rt(int u,int f) {
sz[u] = 1;
int res = 0;
REP(u) if(f^u && !vis[v]) find_rt(v,u) ,sz[u] += sz[v], res = max(res, sz[v]);
res = max(res , sum -sz[u]);
if(res < minn) minn = res ,rt = u ;
}
void dfs(int u) {
vis[u] = 1;
sz[u] = sum + 1;
c[0][u].resize(sz[u] + 1);
c[1][u].resize(sz[u] + 1);
REP(u) if(!vis[v]) {
sum = sz[v], rt = 0 , minn = inf;
find_rt(v,0);
fa[rt] = u;
dfs(rt);
}
}
void modify(int u,int w) {
for(int i = u;i;i =fa[i]) upd(i, 0, get_dis(u,i), w);
for(int i = u;fa[i];i = fa[i]) upd(i, 1, get_dis(u,dis[i]), w);
}
int main()
{
int opt, x, y;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&val[i]);
for(int i=1;i< n;i++) scanf("%d%d",&x,&y), add(x,y), add(y,x);
dfs0(1,0);
get_ol();
sum = N, minn = inf;
find_rt(1,0);
dfs(rt);
for(int i=1;i<=n;i++) modify(i,val[i]);
while(m -- ) {
scanf("%d%d%d",&opt,&x,&y);
x ^= ans , y ^= ans;
if(!opt) {
ans = 0;
ans += qry(x,0,y);
for(int i=x;fa[i];i = fa[i]) {
int dis = get_dis(x,fa[i]);
if(y >= dis) ans += qry(fa[i], 0, y - dis) - qry(i, 1, y - dis);
}
printf("%d\n",ans);
}
else modify(x, y - val[x]), val[x] = y;
}
return 0;
}
P3241
想想,没看出来这题跟上一题有什么区别,然后看了题解才发现有区别。
原本想看看题解,然后思考了一下,从维度的角度来看,这题只是和上一题交换了一下维度,那么我代码里面也交换一下维度那不就好了吗,原来那个桶拿去交换一下,存年龄的信息,无论说你是要取点还是取区间都是一样的,无非只是取的时候,换一下细节上的方法罢了,如此即可,很多条件改一改都是一样的结果,换汤不换药;然后因为那个dist的信息是会变动的,那么就可以根据重心树来存下来,那不就完事了嘛!
注意一些小细节就完事了;
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 150010, M = N * 2;
int n, m ,A ;
int h[N], e[M] , w[M], ne[M], idx;
int age[N];
bool st[N];
struct father {
int u, sum;
LL dist;
};
vector<father> f[N];
struct son{
int age;
LL dist;
bool operator < (const son& t) const {
return age < t.age;
}
};
vector<son> son[N][3];
void add(int a,int b,int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int get_size(int u,int fa) {
if(st[u]) return 0;
int res = 1;
for(int i= h[u]; ~i ;i = ne[i])
if(e[i] != fa) res += get_size(e[i],u);
return res;
}
int get_wc(int u,int fa,int tot,int& wc) {
if(st[u]) return 0;
int sum = 1, ms = 0;
for(int i=h[u];~i;i = ne[i]) {
int j = e[i];
if(j == fa) continue;
int t = get_wc(j, u, tot, wc);
ms = max(ms, t);
sum += t;
}
ms = max(ms, tot - sum);
if(ms <= tot / 2) wc = u;
return sum;
}
void get_dist(int u,int fa,LL dist,int wc,int k, vector <son> &p ) {
if (st[u]) return;
f[u].push_back({wc, k ,dist});
p.push_back({age[u],dist});
for(int i=h[u]; ~i; i = ne[i]) {
int j = e[i];
if(j == fa) continue;
get_dist(j, u, dist + w[i], wc, k, p);
}
}
void calc(int u) {
if(st[u]) return;
get_wc(u,-1, get_size(u,-1), u);
st[u] = true;
for(int i =h[u], k = 0; ~i;i = ne[i]) {
int j = e[i];
if (st[j]) continue;
auto &p = son[u][k];
p.push_back({-1,0}), p.push_back({A+1,0});
get_dist(j, -1, w[i], u, k, p);
k ++ ;
sort(p.begin(), p.end());
for(int i= 1; i < p.size(); i ++) p[i].dist += p[i-1].dist;
}
for(int i=h[u];~i ;i = ne[i]) calc(e[i]);
}
LL query(int u,int l,int r) {
LL res = 0;
for(auto&t :f[u]) {
int g = age[t.u];
if(g >= l && g <= r) res += t.dist;
for(int i=0;i < 3;i ++) {
if(i == t.num) continue;
auto &p = son[t.u][i];
if(p.empty()) continue;
int a = lower_bound(p.begin(), p.end(), son({l ,-1})) - p.begin();
int b = lower_boudn(p.begin(), p.end(), son({r + 1, - 1})) - p.begin();
res += t.dist *(b - a) + p[b - 1].dist - p[a- 1].dist;
}
}
for(int i =0;i <3;i ++) {
auto &p = son[u][i];
if(p.empty()) continue;
int a = lower_bound(p.begin(), p.end(), son({l, - 1})) - p.begin();
int b = lower_bound(p.begin(), p.end(), son(r+1 , -1) ) - p.begin();
res += p[b-1].dist - p[a- 1].dist;
}
return res;
}
P3345
这题还是让我看题解都看了很久的一道题目,还是很难的,看看发现写题解的都是一些金钩选手;
就是维护一棵树的动态的带权重心;
1
e
5
1e5
1e5 级别的数据范围;
首先题目中是没有让你给出为什么是带权重心的,但是,你用切比雪夫不等式反证一下,发现怎么变换你选的那个位置,都不如带权重心来的好,于是我们便知道了我们要求什么东西了;
那么带权重心考虑一下最垃圾的一种求法,就是直接深搜一遍,但是每次我们只会去改一个点呀,那么就考虑一下点分树。为什么说我们一定要使用点分树而不用别的东西来做这题,因为点分树能维持树的log级别深度,以此,让时间复杂度得到相应的保证。然后看下题解里面是怎么说的,根据树的度数,以及复杂度的分析,推测出来有可能是一种通过枚举节点的做法来实现,然后推下,发现贪心规律,然后这题就做出来了,很讲究很巧妙的一道好题。本质上就是要不去搞信息的浪费,每次波及到的点,尽可能的把它缩小来,在树上通常就是将影响缩小到logn级别的,然后再尽量在logn的时间范围内解决就好了;
又觉得有哪里不对,于是又去看了一个小时才把它看懂;真是bitch极了;