动态点分治

动态点分治,带修的。
显然,如果说单纯使用点分治的做法是会超时的,那么我们只要考虑一下继承就好了,别的好像就没什么了。
分析一下整过过程,我们只是改了仅仅一个点,就需要对全局进行重新统计,显然不好,那么修改过得那个点,它的影响范围就是从那个点一直跑上去的一路上的重心,做到这里其实就方便很多了;
然后呗就算一下贡献,加上减去相应的贡献,就能得出正确答案了,以上;
其实在线的不在线的都是一样的,都是那么点东西,分治一下就完事了,无非是一个单独分治一个点,一个要全部分治一遍;

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极了;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值