BZOJ 3589 动态树 树链拆分+纳入和排除定理
标题效果:鉴于一棵树。每个节点有一个右值,所有节点正确启动值他们是0。有两种操作模式,0 x y代表x右所有点的子树的根值添加y。
1 k a1 b1 a2 b2 ……ak bk代表质疑。
共同拥有者k边缘( k <= 5),这些边保证是一个点到根节点的路径上的一段。
问这些路径段上的点的权值和是多少,可能有多条路径重叠的情况。
思路:子树改动,区间查询,非常明显用树链剖分解决,树链剖分维护一个size域。那么x的子树的范围就是pos[x]到pos[x] + size[x] - 1这一段上。能够用线段树区间改动。
关键是查询的时候。单查一条链肯定没什么问题。
可是假设几条链有交集的话就麻烦了。可是依据容斥原理我们知道,当我们把全部的路径都加过一次之后,两个路径重合的部分就计算重了。减掉。之后三个路径重合的部分减多了。再加上……我们仅仅要求出单个链的,两个路径重合的部分,三个路径重合的部分……这样就能够知道答案了。
怎样求多个链相交呢?我们先考虑两个链相交。因为每个路径保证是一个点到根节点的路径上的一段,那么两个链相交仅仅有一种情况。
例如以下图。
链1 2 3 4和2 3 5 6的交集就是2 3 。
观察一下。事实上3是4和6的LCA。2是两个链的顶端较深的那一个。不存在交集的情况就是链底的LCA深度深于当中的一条链的链顶。
多画几个图发现真的是这样。(事实上我仅仅是不会证明罢了。
。。
然后从1到1 << 5枚举取全部边的情况,计算取到n条边时的相交情况,n是计数就加上,是偶数就减去。
CODE:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 200010
#define LEFT (pos << 1)
#define RIGHT (pos << 1|1)
#define CNT (r - l + 1)
using namespace std; pair<int,int> ask[MAX]; struct SegmentTree{
int sum,c;
}tree[MAX << 2]; int points,asks;
int head[MAX],total;
int next[MAX],aim[MAX]; int son[MAX],size[MAX],father[MAX],deep[MAX];
int pos[MAX],top[MAX],cnt; inline void Add(int x,int y)
{
next[++total] = head[x];
aim[total] = y;
head[x] = total;
} void PreDFS(int x,int last)
{
father[x] = last;
deep[x] = deep[last] + 1;
size[x] = 1;
for(int i = head[x]; i; i = next[i]) {
if(aim[i] == last) continue;
PreDFS(aim[i],x);
size[x] += size[aim[i]];
if(size[aim[i]] > size[son[x]])
son[x] = aim[i];
}
} void DFS(int x,int _top)
{
pos[x] = ++cnt;
top[x] = _top;
if(son[x]) DFS(son[x],_top);
for(int i = head[x]; i; i = next[i]) {
if(aim[i] == father[x] || aim[i] == son[x]) continue;
DFS(aim[i],aim[i]);
}
} inline void PushDown(int pos,int cnt)
{
if(tree[pos].c) {
tree[LEFT].c += tree[pos].c;
tree[RIGHT].c += tree[pos].c;
tree[LEFT].sum += tree[pos].c * (cnt - (cnt >> 1));
tree[RIGHT].sum += tree[pos].c * (cnt >> 1);
tree[pos].c = 0;
}
} void Modify(int l,int r,int x,int y,int pos,int c)
{
if(l == x && r == y) {
tree[pos].c += c;
tree[pos].sum += CNT * c;
return ;
}
PushDown(pos,CNT);
int mid = (l + r) >> 1;
if(y <= mid) Modify(l,mid,x,y,LEFT,c);
else if(x > mid) Modify(mid + 1,r,x,y,RIGHT,c);
else {
Modify(l,mid,x,mid,LEFT,c);
Modify(mid + 1,r,mid + 1,y,RIGHT,c);
}
tree[pos].sum = tree[LEFT].sum + tree[RIGHT].sum;
} int Ask(int l,int r,int x,int y,int pos)
{
if(l == x && r == y)
return tree[pos].sum;
PushDown(pos,CNT);
int mid = (l + r) >> 1;
if(y <= mid) return Ask(l,mid,x,y,LEFT);
if(x > mid) return Ask(mid + 1,r,x,y,RIGHT);
int left = Ask(l,mid,x,mid,LEFT);
int right = Ask(mid + 1,r,mid + 1,y,RIGHT);
return left + right;
} inline int Ask(int x,int y)
{
int re = 0;
while(top[x] != top[y]) {
if(deep[top[x]] < deep[top[y]])
swap(x,y);
re += Ask(1,cnt,pos[top[x]],pos[x],1);
x = father[top[x]];
}
if(deep[x] < deep[y]) swap(x,y);
re += Ask(1,cnt,pos[y],pos[x],1);
return re;
} inline int GetLCA(int x,int y)
{
while(top[x] != top[y]) {
if(deep[top[x]] < deep[top[y]])
swap(x,y);
x = father[top[x]];
}
return deep[x] < deep[y] ? x:y;
} inline bool Merge(pair<int,int> &a,pair<int,int> b)
{
if(deep[a.first] < deep[a.second]) swap(a.first,a.second);
if(deep[b.first] < deep[b.second]) swap(b.first,b.second);
int lca = GetLCA(a.first,b.first);
if(deep[a.second] > deep[lca] || deep[b.second] > deep[lca]) return false;
a.first = lca;
a.second = deep[a.second] > deep[b.second] ? a.second:b.second;
return true;
} inline int Calc(int cnt,int status)
{
int p = 0;
for(int i = 0; i < cnt; ++i)
p += (status >> i)&1;
p = p&1 ? 1:-1;
pair<int,int> now(0,0);
for(int i = 0; i < cnt; ++i)
if((status >> i)&1) {
if(!now.first) now = ask[i + 1];
else if(!Merge(now,ask[i + 1])) return 0;
}
//cout << status << ' ' << now.first << ' ' << now.second << ' ' << Ask(now.first,now.second) << ' ' << p << endl;
return p * Ask(now.first,now.second);
} inline int MainTask(int cnt)
{
int re = 0;
for(int i = 1; i <= cnt; ++i)
scanf("%d%d",&ask[i].first,&ask[i].second);
for(int i = 1; i < (1 << cnt); ++i)
re += Calc(cnt,i);
return re;
} int main()
{
cin >> points;
for(int x,y,i = 1; i < points; ++i) {
scanf("%d%d",&x,&y);
Add(x,y);
}
PreDFS(1,0);
DFS(1,1);
cin >> asks;
for(int flag,i = 1; i <= asks; ++i) {
scanf("%d",&flag);
if(!flag) {
int x,y;
scanf("%d%d",&x,&y);
Modify(1,cnt,pos[x],pos[x] + size[x] - 1,1,y);
}
else {
int cnt;
scanf("%d",&cnt);
printf("%d\n",MainTask(cnt)&0x7fffffff);
}
}
return 0;
}
版权声明:本文博客原创文章,博客,未经同意,不得转载。
BZOJ 3589 动态树 树链拆分+纳入和排除定理的更多相关文章
- BZOJ 3589 动态树(子树操作,链查询)
题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3589 题意:给出一棵有根树,两种操作:(1)以u为根的子树所有节点权值加上一个数字 ...
- BZOJ 3589 动态树 (树链剖分+线段树)
前言 众所周知,90%90\%90%的题目与解法毫无关系. 题意 有一棵有根树,两种操作.一种是子树内每一个点的权值加上一个同一个数,另一种是查询多条路径的并的点权之和. 分析 很容易看出是树链剖分+ ...
- BZOJ 3589: 动态树 树链剖分+线段树+树链的并
利用树剖序的一些性质~ 这个题可以出到 $\sum k=10^5$ 左右. 做法很简单:每次暴力跳重链,并在线段树上查询链和. 查询之后打一个标记,把加过的链都置为 $0$.这样的话在同一次询问时即使 ...
- bzoj 3589: 动态树【树链剖分+容斥】
因为一开始调试不知道unsigned怎么输出就没有加\n结果WA了一上午!!!!!然而最后放弃了unsigned选择了&2147483647 首先链剖,因为它所给的链一定是某个点到根的路径上的 ...
- 【BZOJ-3589】动态树 树链剖分 + 线段树 + 线段覆盖(特殊的技巧)
3589: 动态树 Time Limit: 30 Sec Memory Limit: 1024 MBSubmit: 405 Solved: 137[Submit][Status][Discuss] ...
- hdu5044 Tree 树链拆分,点细分,刚,非递归版本
hdu5044 Tree 树链拆分.点细分.刚,非递归版本 //#pragma warning (disable: 4786) //#pragma comment (linker, "/ST ...
- 【BZOJ3589】动态树 树链剖分+线段树
Description 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0: 这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1: 小明希望你 ...
- 【BZOJ】3319: 黑白树(并查集+特殊的技巧/-树链剖分+线段树)
http://www.lydsy.com/JudgeOnline/problem.php?id=3319 以为是模板题就复习了下hld............................. 然后n ...
- bzoj 3881 [Coci2015]Divljak fail树+树链的并
题目大意 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: "1 P",Bob往自己的集合里添 ...
随机推荐
- 查询oracle表字段信息
表字段的信息咱们可以称之为元数据,今天有人问怎么把表字段的信息导出来,说实话我还不会用plsql develper把表的结构导出来,像下图所示: 在写数据库设计说明书的时候,想要把这个表格拷贝出来,这 ...
- wscript:329: error: Could not autodetect OpenSSL support. Make sure OpenSSL development packages are
安装node错: wscript:329: error: Could not autodetect OpenSSL support. Make sure OpenSSL development pac ...
- Sort方法的扩展
OC中类方法中仅仅为我们提供了一些降序方法,如今我们自定义方法,实现升序. 1.要求:定义一个Person类,实例变量包含name,age,height,定义几个对象,把这些对象保存在数组中,自定义方 ...
- C++头文件保护符和变量的声明定义
1.#ifndef #define #endif头文件保护符 在编译的过程中,每个.cpp文件被看成一个单独的文件来编译成单独的编译单元,#ifndef 保证类的头文件在同一个.cpp文件里被多次引用 ...
- Matlab hermite
保形分段三次hermite插值 % 这是MATLAB里面的pchip.m文件.这里把它的凝视改写成汉语,主要是想弄清楚它是怎么计算在节点处的导数的. function v = pchip(x,y,xx ...
- DWR异步产生的问题
默认情况下,DRW是异步的.当数据量大的时候,数据还未加载完就已经提交了.这样会照成数据丢失.为了解决这个问题应该改变DWR的数据加载方式,改为同步的.这样就不会照成数据丢失. DWREngine.s ...
- sharepoint 2013 使用powershell更改站点集配额和锁定
打开sharepoint powershell 2013,使用管理员方式打开 逐行输入下面命令: $Admin = new-object Microsoft.SharePoint.Administr ...
- Java引进和应用的包装类
Java介绍包装类: 于Java它设计主张的想法,也就是说,一切都是对象.但是,我们知道,,Java数据类型分为基本数据类型和引用数据类型,但基本的数据怎么能成对象?为了解决这个问题,对需要8一个类的 ...
- win8/win10/win2012r2 存储池 冗余分析
StorageSpace:a. Simple,相当于RAID0,无冗余,不考虑b. Two-way Mirror,双重镜像,至少2块盘,性能单盘,可以坏一块盘c. Three-way Mirror,三 ...
- SpringMVC单文件上传、多文件上传、文件列表显示、文件下载(转)
林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 本文详细讲解了SpringMVC实例单文件上传.多文件上传.文件列表显示.文件下载. 本文工程 ...