BZOJ 2783 JLOI 2012 树 乘+二分法

标题效果:鉴于一棵树和一个整数s,问中有树木几个这样的路径,点和担保路径==s,深度增量点。

这一数额的输出。

思维:用加倍的想法,我们可以O(logn)在时间找点他第一n。因为点权仅仅能是正的,满足二分性质。然后对于每个点二分。看看有没有路径的权值和是S。统计答案,输出。

CODE:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 100010
using namespace std;

int points,s;
int src[MAX];
int head[MAX],total;
int next[MAX << 1],aim[MAX << 1];

int father[MAX][20],length[MAX][20];

inline void Add(int x,int y);
void DFS(int x,int last);
void SparseTable();

inline bool Judge(int x);
inline int GetLength(int x,int deep);

int main()
{
	cin >> points >> s;
	for(int i = 1;i <= points; ++i)
		scanf("%d",&src[i]);
	for(int x,y,i = 1;i < points; ++i) {
		scanf("%d%d",&x,&y);
		Add(x,y),Add(y,x);
	}
	DFS(1,0);
	SparseTable();
	int ans = 0;
	for(int i = 1;i <= points; ++i)
		ans += Judge(i);
	cout << ans << endl;
	return 0;
}

inline void Add(int x,int y)
{
	next[++total] = head[x];
	aim[total] = y;
	head[x] = total;
}

void DFS(int x,int last)
{
	father[x][0] = last;
	length[x][0] = src[last];
	for(int i = head[x];i;i = next[i]) {
		if(aim[i] == last)	continue;
		DFS(aim[i],x);
	}
}

void SparseTable()
{
	for(int j = 1;j <= 19; ++j)
		for(int i = 1;i <= points; ++i) {
			father[i][j] = father[father[i][j - 1]][j - 1];
			length[i][j] = length[i][j - 1] + length[father[i][j - 1]][j - 1];
		}
}

inline bool Judge(int x)
{
	int l = 0,r = 100000;
	while(l <= r) {
		int mid = (l + r) >> 1;
		int length = GetLength(x,mid) + src[x];
		if(length < s)	l = mid + 1;
		else if(length > s)	r = mid - 1;
		else	return true;
	}
	return false;
}

inline int GetLength(int x,int deep)
{
	int re = 0;
	for(int i = 19; ~i; --i)
		if(deep - (1 << i) >= 0) {
			deep -= 1 << i;
			re += length[x][i];
			x = father[x][i];
		}
	return re;
}

版权声明:本文博客原创文章,博客,未经同意,不得转载。

时间: 2024-08-01 10:32:15

BZOJ 2783 JLOI 2012 树 乘+二分法的相关文章

BZOJ 2783 JLOI 2012 树 倍增+二分

题目大意:给出一棵树和一个整数s,问在树上有几条这样路径,保证路径上的点权和==s,点的深度递增.输出这个数量. 思路:利用倍增的思想,我们能在O(logn)的时间内求出一个点到他的第n个爸爸之间所有点的点权之和.由于点权只能是正的,满足二分性质.然后对于每一个点二分,看看有没有路径的权值和是S,统计答案,输出. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <alg

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

【BZOJ】3319: 黑白树

http://www.lydsy.com/JudgeOnline/problem.php?id=3319 题意:给一棵n节点的树(n<=1e6),m个操作(m<=1e6),每次操作有两种:1.查询u到根的第一条黑边的编号.2.将u到v的路径全部染成黑色 #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream>

BZOJ 1912 巡逻(树直径)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1912 题意:给出一棵树,边权为1.现在加一条或两条边后,使得从1出发遍历每个点至少一次再回到1的路程最短. 思路:先求一次树的直径Max1.然后将直径的边权改为-1,再求一次直径Max2.答案为ans=(n-1)*2-(Max1-1)-(Max2-1). struct node { int u,v,w,next; }; node edges[N<<1]; int head[N],e;

BZOJ 2657 ZJOI 2012 旅游(journey) 树的直径

题目大意:给出一个凸多边形的三角剖分图,每一个三角形代表一个城市,现在连接这个图中的两个点,问最多能够经过多少个城市. 思路:浙江都是一帮神么.. 这题给的条件简直是不知所云啊..转化十分巧妙.因为每个凸n边形经过三角剖分之后会出现n - 2个三角形,任意一条边只会成为两个城市的公共边或者整个多边形的边.不难推出两个城市的公共边是n - 3条,也就是说把公共边看成是新图的边的话,就会新图就会构成一颗树.之后就是很水的树的直径了... CODE: #include <map> #include

BZOJ 2783 树

树上倍增. #include<iostream> #include<cstdio> #include<cstring> #define maxv 100500 #define maxe 200500 using namespace std; struct edge { int v,nxt; }e[maxe]; int n,s,w[maxv],dis[maxv],x,y,nume=0,g[maxv],root,anc[maxv][20],sum[maxv][20],ans

BZOJ 2783 JLOI2012 树 DFS

题目大意:给定一棵有根树,每个节点有权值,求有多少链上的权值和为S,要求链上节点的深度必须单调(即这条链由某个节点出发指向根) DFS一遍,当深搜到一个点时将这个点加入队列,同时队头向后调整,使队列中元素之和<=s,记录ans 当一个点出栈时将队尾删除,同时队头向前调整,使队列中元素之和刚好<=s 这题1s略卡时间...不过我旁边的哥们用nlogn的算法超时700ms过去的0.0 这怎么过去的0.0 误差也太大了吧0.0 #include<cstdio> #include<c

BZOJ 2783 [JLOI2012]树

题解:set就好 #include<iostream> #include<cstdio> #include<map> #include<cstring> using namespace std; const int maxn=100009; int n,k; int root; int w[maxn]; int notroot[maxn]; int ans; int cntedge; int head[maxn]; int to[maxn<<1]

Bzoj 2789: [Poi2012]Letters 树状数组,逆序对

2789: [Poi2012]Letters Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 278  Solved: 185[Submit][Status][Discuss] Description 给出两个长度相同且由大写英文字母组成的字符串A.B,保证A和B中每种字母出现的次数相同. 现在每次可以交换A中相邻两个字符,求最少需要交换多少次可以使得A变成B. Input 第一行一个正整数n (2<=n<=1,000,000),表示字符串的长度