Dynamic point divide and conquer

Posted by 25lez25 on Wed, 26 Jan 2022 21:20:52 +0100

Dynamic point divide and conquer, with repair.
Obviously, if the simple use of point divide and conquer will timeout, then we just need to consider inheritance, and there seems to be nothing else.
Analyzing the whole process, we need to re count the overall situation if we only change one point. Obviously, it's not good. Then the modified point has the impact range of the center of gravity along the way from that point. In fact, it's much more convenient to do here;
Then calculate the contribution, add and subtract the corresponding contribution, and you can get the correct answer, above;
In fact, both online and offline are the same. There are so many things. It's all over once. It's just a single point and a whole point;

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

Think about it. I didn't see the difference between this question and the previous one. Then I saw the solution and found the difference.
Originally, I wanted to see the solution of the problem, and then thought about it. From the perspective of dimension, this problem only exchanged dimensions with the previous problem, so I also exchanged dimensions in my code. Wouldn't it be good if I took the bucket to exchange the information of age, whether you want to take points or intervals, it's just the time to take it, Just change the method of details. In this way, many conditions are the same as the result. Change the soup without changing the dressing; Then, because the dist information will change, you can save it according to the center of gravity tree, and it's over!
Pay attention to some small details and it's over;

#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

This problem is still a problem that I've been looking at for a long time. It's still very difficult. I found that some gold hook players wrote the problem solution;
Is to maintain the dynamic weight center of a tree; 1 e 5 1e5 1e5 level data range;
First of all, the title does not ask you to give why it is the weighted center of gravity. However, you can prove it by using Chebyshev inequality. You find that how to change the position you choose is not as good as the weighted center of gravity, so we know what we want;
Then, the most garbage solution to consider with the weight focus is to search directly, but each time we only change one point, then consider the point sub tree. Why do we have to use a point tree instead of anything else to do this problem? Because the point tree can maintain the log level depth of the tree, so that the time complexity can be guaranteed accordingly. Then look at what is said in the problem solution. According to the degree of the tree and the analysis of complexity, it is speculated that it may be realized by enumerating nodes. Then push it down to find the law of greed, and then the problem is done. It is a very exquisite and ingenious good problem. In essence, it means not to waste information. Every time the point is affected, reduce it as much as possible. In the tree, usually reduce the impact to the logn level, and then try to solve it within the time range of logn;
I thought there was something wrong, so I went to read it for another hour to understand it; It's a great bitch;

Topics: Algorithm data structure Graph Theory