分治算法专题训练二

lgP4178 Tree

题意:image-20211030143405539

sol.点分治板子题

#include<bits/stdc++.h>
#define MAXN 50005
#define INF 0x3f3f3f3f
using namespace std;

int n,K,h[MAXN],tot;
struct node{int from,to,next,cost;}e[MAXN << 1];
void add(int x , int y , int z){
	tot++;
	e[tot].from = x;
	e[tot].to = y;
	e[tot].cost = z;
	e[tot].next = h[x];
	h[x] = tot;
}
int sz[MAXN],val[MAXN],vis[MAXN],zx,dis[MAXN];
int js,jjs,dui[MAXN],in[10000005],ans[MAXN];

void get_sz(int now , int fa){
	sz[now] = 1;
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(e[i].to == fa)continue;
		if(vis[e[i].to])continue;
		get_sz(e[i].to , now);
		sz[now] += sz[e[i].to];
	}
}

void get_zx(int now , int fa , int yl){
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(e[i].to == fa)continue;
		if(vis[e[i].to])continue;
		get_zx(e[i].to , now , yl);
		val[now] = max(val[now] , sz[e[i].to]);
	}
	val[now] = max(val[now] , sz[yl] - sz[now]);
	if(val[zx] > val[now])zx = now;
}


void get_dis(int now , int fa){
	if(dis[now] <= K)dui[++js] = dis[now];
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(e[i].to == fa)continue;
		if(vis[e[i].to])continue;
		dis[e[i].to] = dis[now] + e[i].cost;
		get_dis(e[i].to , now);
	}
}

void cal(int now){
	js = jjs = 0;
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(vis[e[i].to])continue;
		jjs = js;
		dis[e[i].to] = e[i].cost;
		get_dis(e[i].to , now);
		for(int j = 1 ; j <= K ; j++){
			if(!in[j])continue;
			for(int p = jjs + 1 ; p <= js ; p++){
				if(j + dui[p] > K)continue;
				ans[j + dui[p]] += in[j];
			}
		}
		for(int p = jjs + 1 ; p <= js ; p++)in[dui[p]]++;
	}
	for(int i = 1 ; i <= js ; i++)ans[dui[i]]++;
	for(int i = 1 ; i <= js ; i++)in[dui[i]] = 0;
}

void solve(int now){
	cal(now) , vis[now] = 1;
	get_sz(now , now);
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(vis[e[i].to])continue;
		zx = 0 , get_zx(e[i].to , now , e[i].to);
		solve(zx);
	}
}

int main(){
	val[0] = INF;
	memset(h , -1 , sizeof(h)) , tot = 0;
	scanf("%d" , &n);int x,y,z;
	for(int i = 1 ; i < n ; i++){
		scanf("%d%d%d" , &x , &y , &z);
		add(x , y , z) , add(y , x , z);
	}
	scanf("%d" , &K);
	zx = 0;
	get_sz(1 , 1);
	get_zx(1 , 1 , 1);
	solve(zx);
	int sum = 0;
	for(int i = 1 ; i <= K ; i++)sum += ans[i];
	cout<<sum<<endl;
} 

lgP2634

题意:给你一颗带边权的树。询问有多少对点(u,v)的路径长度是3的倍数

sol.点分治板子。打着练一下手

#include<bits/stdc++.h>
#define MAXN 40005
#define INF 0x3f3f3f3f
using namespace std;

int n,h[MAXN],tot,ans;
struct node{int from,to,next,cost;}e[MAXN << 1];
void add(int x , int y , int z){
	tot++;
	e[tot].from = x;
	e[tot].to = y;
	e[tot].cost = z;
	e[tot].next = h[x];
	h[x] = tot;
}

int val[MAXN],sz[MAXN],zx,vis[MAXN];
int dis[MAXN],cnt[5],cnt2[5];

void get_sz(int now , int fa){
	sz[now] = 1;
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(e[i].to == fa)continue;
		if(vis[e[i].to])continue;
		get_sz(e[i].to , now);
		sz[now] += sz[e[i].to];
	}
}

void get_zx(int now , int fa , int yl){
	val[now] = 0;
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(e[i].to == fa)continue;
		if(vis[e[i].to])continue;
		get_zx(e[i].to , now , yl);
		val[now] = max(val[now] , sz[e[i].to]);
	}
	val[now] = max(val[now] , sz[yl] - sz[now]);
	if(val[zx] > val[now])zx = now;
}

void get_dis(int now , int fa){
	cnt2[dis[now] % 3]++;
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(e[i].to == fa)continue;
		if(vis[e[i].to])continue;
		dis[e[i].to] = dis[now] + e[i].cost;
		get_dis(e[i].to , now);
	}
}

void cal(int now){
	for(int i = 0 ; i <= 2 ; i++)cnt[i] = cnt2[i] = 0;
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(vis[e[i].to])continue;
		for(int j = 0 ; j <= 2 ; j++)cnt2[j] = 0;
		dis[e[i].to] = e[i].cost;
		get_dis(e[i].to , now);
		for(int j = 1 ; j <= 2 ; j++)ans = ans + cnt[3 - j] * cnt2[j];
		ans = ans + cnt2[0] * cnt[0];
		for(int j = 0 ; j <= 2 ; j++)cnt[j] += cnt2[j];
	}
	ans = ans + cnt[0];
}

void solve(int now){
	vis[now] = 1;
	cal(now);
	get_sz(now , now);
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(vis[e[i].to])continue;
		zx = 0 , get_zx(e[i].to , now , e[i].to);
		solve(zx);
	}
}

int gcd(int a , int b){
	if(!b)return a;
	return gcd(b , a % b);
}

namespace io {
	const int SIZE = 1 << 22 | 1;
	char iBuf[SIZE], *iS, *iT, c;
	char oBuf[SIZE], *oS = oBuf, *oT = oBuf + SIZE;
	#define gc() (iS == iT ? iT = iBuf + fread(iS = iBuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS++) : *iS++)
	template<class I> void read(I &x) {
		int f = 1;
		for(c = gc(); c < '0' || c > '9'; c = gc())
			if(c == '-') f = -1;
		for(x = 0; c >= '0' && c <= '9'; c = gc())
			x = (x << 3) + (x << 1) + (c & 15);
		x *= f;
	}
	inline void flush () {
		fwrite(oBuf, 1, oS - oBuf, stdout);
		oS = oBuf;
	}
	inline void putc(char x) {
		*oS++ = x;
		if(oS == oT) flush();
	}
	template<class I> void print(I x) {
		if(x < 0) putc('-'), x = -x;
		static char qu[55];
		char *tmp = qu;
		do *tmp++ = (x % 10) ^ '0'; while(x /= 10);
		while(tmp-- != qu) putc(*tmp);
		putc('\n');
	}
	struct flusher{ ~flusher() { flush(); } }_;
}


int main(){
	memset(h , -1 , sizeof(h)) , tot = 0 , val[0] = INF;
	int x,y,z;
	io :: read(n);
	for(int i = 1 ; i < n ; i++){
		io :: read(x);
		io :: read(y);
		io :: read(z);
		add(x , y , z);
		add(y , x , z);
	}
	get_sz(1 , 1);
	zx = 0 , get_zx(1 , 1 , 1);
	solve(zx);
	int zz = gcd(ans * 2 + n , n * n);
	cout<<(ans * 2 + n) / zz<<"/"<<n * n / zz<<endl;
}

lgP4149 [IOI2011]Race

题意:image-20211030153620090

sol.点分治板子。打着练一下手

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MAXN 5000005
using namespace std;

int n,K,ans = INF;
int h[MAXN],tot;
struct node{int from,to,next,cost;}e[MAXN << 1];
void add(int x , int y , int z){
	tot++;
	e[tot].from = x;
	e[tot].to = y;
	e[tot].cost = z;
	e[tot].next = h[x];
	h[x] = tot;
}
int vis[MAXN],dis[MAXN],sum[MAXN];
int sz[MAXN],val[MAXN],zx;

void get_sz(int now , int fa){
	sz[now] = 1;
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(e[i].to == fa)continue;
		if(vis[e[i].to])continue;
		get_sz(e[i].to , now);
		sz[now] += sz[e[i].to];
	}
}

void get_zx(int now , int fa , int yl){
	val[now] = 0;
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(e[i].to == fa)continue;
		if(vis[e[i].to])continue;
		get_zx(e[i].to , now , yl);
		val[now] = max(val[now] , sz[e[i].to]);
	}
	val[now] = max(val[now] , sz[yl] - sz[now]);
	if(val[now] < val[zx])zx = now;
}

int len;
int q[MAXN],in[MAXN];

void get_dis(int now , int fa){
	if(dis[now] <= K)len++ , q[len] = now;
	if(dis[now] == K && ans > sum[now]){
		ans = min(ans , sum[now]);	
	}
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(e[i].to == fa)continue;
		if(vis[e[i].to])continue;
		dis[e[i].to] = dis[now] + e[i].cost;
		sum[e[i].to] = sum[now] + 1;
		get_dis(e[i].to , now);
	}
}

void cal(int now){
	int zz;len = 0;
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(vis[e[i].to])continue;
		zz = len , dis[e[i].to] = e[i].cost , sum[e[i].to] = 1;
		get_dis(e[i].to , now);
		for(int j = zz + 1 ; j <= len ; j++)ans = min(ans , in[K - dis[q[j]]] + sum[q[j]]);
		for(int j = zz + 1 ; j <= len ; j++)in[dis[q[j]]] = min(in[dis[q[j]]] , sum[q[j]]);
	}
	for(int i = 1 ; i <= len ; i++)in[dis[q[i]]] = INF;
}

void solve(int now){
	vis[now] = 1;
	cal(now);
	get_sz(now , now);
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(vis[e[i].to])continue;
		zx = 0 , get_zx(e[i].to , now , e[i].to);
		solve(zx);
	}
}

int main(){
	val[0] = INF;
	memset(in , 0x3f , sizeof(in));
	memset(h , -1 , sizeof(h)) , tot = 0;
	scanf("%d%d" , &n , &K);int x,y,z;
	for(int i = 1 ; i < n ; i++){
		scanf("%d%d%d" , &x , &y , &z) , x++ , y++;
		add(x , y , z) , add(y , x , z);
	}
	get_sz(1 , 1);
	zx = 0 , get_zx(1 , 1 , 1);
	solve(zx);
	if(ans == INF)cout<<-1<<endl;
	else cout<<ans<<endl;
} 

BZOJ3697

题意:给出一棵树,边权0/1。

称一条路是“平衡”的,其中0边和1边数量相同。

现在询问有多少条边“双平衡路径”,即存在一个中转点,从起点到中转点,从中转点到终点的路径都是“平衡的” , n<=1e4

sol.就点分值维护一个东西吧,(由于没地方交,我就嘴巴写写吧)


cf448C

直接就是一个分治裸题

#include<bits/stdc++.h>
#define MAXN 5005
#define INF 0x3f3f3f3f
using namespace std;

int n,h[MAXN];
int minl[MAXN][25],lg[MAXN];

int que(int l , int r){
	int zz = lg[r - l + 1];
	if(h[minl[l][zz]] < h[minl[r - (1 << zz) + 1][zz]])return minl[l][zz];
	else return minl[r - (1 << zz) + 1][zz];
}

int solve(int l , int r , int v){
	if(l > r)return 0;
	if(l == r)return v != h[l];
	int zz1 = INF , zz2 = 0;
	int mid = que(l , r);
	return min(solve(l , mid - 1 , h[mid]) + solve(mid + 1 , r , h[mid]) + h[mid] - v , r - l + 1);
}

int main(){
	//freopen("out.txt" , "r" , stdin);
	scanf("%d" , &n);for(int i = 1 ; i <= n ; i++)scanf("%d" , &h[i]);
	lg[0] = (-1);for(int i = 1 ; i <= n ; i++)lg[i] = lg[i >> 1] + 1;
	for(int i = 1 ; i <= n ; i++)minl[i][0] = i;
	for(int j = 1 ; j <= 20 ; j++){
		for(int i = 1 ; i + (1 << j) - 1 <= n ; i++){
			if(h[minl[i][j - 1]] <= h[minl[i + (1 << (j - 1))][j - 1]])minl[i][j] = minl[i][j - 1];
			else minl[i][j] = minl[i + (1 << (j - 1))][j - 1];
		}
	}
	cout<<solve(1 , n , 0)<<endl;
	
}

也有O(n)的贪心暴力做法,不过也就那样。。。。好像还是noip2018T1原题

CF475D

题意:给你一个长度为n(n<=1e5)的序列,一个询问q(q<=3e5),一次询问让你求 区间[lr]的个数,使得gcd(al,al+1,...,ar)=xi

sol.考虑你 固定左端点,移动右端点的整个过程里, gcd只会发生log次变化

考虑如何处理这个问题

不妨将所有询问离线下来,然后对于原来的序列 有整一个区间中点mid

考虑 每个区间都经过这个区间中间的答案,每次统计因为 gcd只会发生log次变化,可以直接开个桶,直接做就好了

复杂度大概是O(nlog2n)

#include<bits/stdc++.h>
#define MAXN 300005
#define INF 0x3f3f3f3f
using namespace std;

int gcd(int x , int y){
	if(!y)return x;
	return gcd(y , x % y);
}

int n,a[MAXN],b[MAXN],Q;
int len1,len2;
struct node{int val,num;}t1[MAXN],t2[MAXN];
map<int , int>ans;

void solve(int l , int r){
	if(r < l)return;
	int mid = (l + r) >> 1;
	b[mid] = a[mid];
	for(int i = mid - 1 ; i >= l ; i--)b[i] = gcd(a[i] , b[i + 1]);
	for(int i = mid + 1 ; i <= r ; i++)b[i] = gcd(a[i] , b[i - 1]);
	len1 = len2 = 0;
	
	for(int i = mid ; i >= l ; i--){
		if(t1[len1].val != b[i])len1++ , t1[len1].val = b[i] , t1[len1].num = 0;
		t1[len1].num++;
	}
	for(int i = mid ; i <= r ; i++){
		if(t2[len2].val != b[i])len2++ , t2[len2].val = b[i] , t2[len2].num = 0;
		t2[len2].num++;
	}
	
	for(int i = 1 ; i <= len1 ; i++){
		for(int j = 1 ; j <= len2 ; j++){
			ans[gcd(t1[i].val , t2[j].val)] += t1[i].num * t2[j].num;
		}
	}
	
	
	solve(l , mid - 1);
	solve(mid + 1 , r);
}

int main(){
	scanf("%d" , &n);
	for(int i = 1 ; i <= n ; i++)scanf("%d" , &a[i]);
	solve(1 , n);
	//for(int i = 1 ; i <= 10 ; i++)cout<<i<<"  "<<ans[i]<<endl;
	int x;
	scanf("%d" , &Q);
	while(Q--){
		scanf("%d" , &x);
		cout<<ans[x]<<endl;
	}
}
posted @   After_rain  阅读(80)  评论(1编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示