博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2018ICPC网络赛(焦作站)E题题解
阅读量:6290 次
发布时间:2019-06-22

本文共 5554 字,大约阅读时间需要 18 分钟。

一、题目链接

二、题意

给定一棵树,有四种操作:

$1\ u\ v\ x$:把节点$u$到$v$路径上的所有点的权值乘以$x$;

$2\ u\ v\ x$:把节点$u$到$v$路径上的所有点的权值加上$x$;

$3\ u\ v$:把节点$u$到$v$路径上的所有点的权值取反(~操作);

$4\ u\ v$:查询节点$u$到$v$路径上的所有点的权值和;

所有操作都需要$mod\ 2^{64}$。

三、思路

操作1、2和4是很裸的树链剖分。关键是操作3。这里有个小技巧:对任意一个数字$x$取反,都等于$-(x+1)$。对于此题的$unsigned\ long\ long$类型,同样适用($-1$就是$1111\cdots1111_{(2)}=2^{64}-1$)。明白这个以后,取反操作可以变成加法和乘法。

然后要注意的是,线段树的区间加和区间乘的顺序问题,这里也有个技巧:如果区间$[l,r]$之前的和是$sum$,这个区间先加了一个$x$,再乘了一个$y$,那么可以变成$sum*y+x*y$。即让这个区间先乘一个$y$,再加上$x*y$。这样就可以保证以先乘后加的顺序不出问题了。

然后这题就愉快地AC了。

结论:对任意一个数$x$取反,无论这个数的类型是什么($int,unsigned\ int,long\ long,unsigned\ long\ long$都可以),都等于$-(x+1)$。

四、代码

 

#include
using namespace std;#define MAXN 100010typedef unsigned long long ULL;struct edge { int to, next; edge(int to = 0, int next = 0): to(to), next(next) {}} es[MAXN * 2];int head[MAXN], ecnt;int f[MAXN], deep[MAXN], siz[MAXN], zson[MAXN], top[MAXN], o2n[MAXN], dfscnt;int n, q;ULL a[MAXN];namespace st { struct node { ULL sum, slazy, plazy; node(ULL a1 = 0, ULL a2 = 0, ULL a3 = 0): sum(a1), slazy(a2), plazy(a3) {} } ns[MAXN * 4]; void init() { for(int i = 0, t = 4 * n + 5; i < t; ++i)ns[i] = node(0, 0, 1); } void pushdown(int rt, int l, int r) { int lch = rt << 1, rch = rt << 1 | 1, mid = (l + r) >> 1; if(ns[rt].plazy != 1 || ns[rt].plazy != 0) { ns[lch].sum = ns[lch].sum * ns[rt].plazy + (mid - l + 1) * ns[rt].slazy; ns[rch].sum = ns[rch].sum * ns[rt].plazy + (r - mid) * ns[rt].slazy; ns[lch].plazy *= ns[rt].plazy; ns[rch].plazy *= ns[rt].plazy; ns[lch].slazy = ns[lch].slazy * ns[rt].plazy + ns[rt].slazy; ns[rch].slazy = ns[rch].slazy * ns[rt].plazy + ns[rt].slazy; ns[rt].plazy = 1, ns[rt].slazy = 0; } } void update(int ul, int ur, ULL x, int type, int rt = 1, int l = 1, int r = n) { if(l > ur || r < ul)return; if(l >= ul && r <= ur) { if(type == 2)ns[rt].sum += x * ULL(r - l + 1), ns[rt].slazy += x; else ns[rt].sum *= x, ns[rt].plazy *= x, ns[rt].slazy *= x; return; } pushdown(rt, l, r); int mid = (l + r) >> 1; if(ul <= mid)update(ul, ur, x, type, rt << 1, l, mid); if(ur > mid)update(ul, ur, x, type, rt << 1 | 1, mid + 1, r); ns[rt].sum = ns[rt << 1].sum + ns[rt << 1 | 1].sum; } ULL query(int ql, int qr, int rt = 1, int l = 1, int r = n) { if(l > qr || r < ql)return 0LL; if(l >= ql && r <= qr)return ns[rt].sum; pushdown(rt, l, r); int mid = (l + r) >> 1; ULL res = 0; if(ql <= mid)res += query(ql, qr, rt << 1, l, mid); if(qr > mid)res += query(ql, qr, rt << 1 | 1, mid + 1, r); return res; }};void add(int from, int to) { es[++ecnt] = edge(to, head[from]), head[from] = ecnt;}void init() { memset(head, 0, sizeof(head[0]) * (n + 3)); ecnt = 0; memset(zson, 0, sizeof(zson[0]) * (n + 3)); dfscnt = 0; st::init();}void dfs1(int root, int par) { deep[root] = deep[par] + 1, f[root] = par, siz[root] = 1; int ms = 0; for(int i = head[root]; i; i = es[i].next) { int to = es[i].to; if(to != par) { dfs1(to, root); siz[root] += siz[to]; if(ms < siz[to]) { ms = siz[to], zson[root] = to; } } }}void dfs2(int root, int par, int tp) { top[root] = tp, o2n[root] = ++dfscnt; st::update(dfscnt, dfscnt, a[root], 2); if(!zson[root])return; dfs2(zson[root], root, tp); for(int i = head[root]; i; i = es[i].next) { int to = es[i].to; if(to != par && to != zson[root]) { dfs2(to, root, to); } }}void update(int u, int v, ULL x, int type) { int tu = top[u], tv = top[v]; while(tu != tv) { if(deep[tu] >= deep[tv]) { st::update(o2n[tu], o2n[u], x, type); u = f[tu], tu = top[u]; } else { st::update(o2n[tv], o2n[v], x, type); v = f[tv], tv = top[v]; } } if(o2n[u] <= o2n[v])st::update(o2n[u], o2n[v], x, type); else st::update(o2n[v], o2n[u], x, type);}ULL query(int u, int v) { int tu = top[u], tv = top[v]; ULL res = 0; while(tu != tv) { if(deep[tu] >= deep[tv]) { res += st::query(o2n[tu], o2n[u]); u = f[tu], tu = top[u]; } else { res += st::query(o2n[tv], o2n[v]); v = f[tv], tv = top[v]; } } if(o2n[u] <= o2n[v])res += st::query(o2n[u], o2n[v]); else res += st::query(o2n[v], o2n[u]); return res;}int main() {// freopen("e.in","r",stdin); int fa, op, u, v; ULL x; while(~scanf("%d", &n)) { init(); for(int i = 2; i <= n; ++i) { scanf("%d", &fa); add(fa, i), add(i, fa); } dfs1(1, 0); dfs2(1, 0, 1); scanf("%d", &q); while(q--) { scanf("%d", &op); if(op == 1) { scanf("%d%d%llu", &u, &v, &x); update(u, v, x, 1); } else if(op == 2) { scanf("%d%d%llu", &u, &v, &x); update(u, v, x, 2); } else if(op == 3) { scanf("%d%d", &u, &v); update(u, v, 1, 2); update(u, v, -1, 1); } else { scanf("%d%d", &u, &v); ULL res = query(u, v); printf("%llu\n", res); } } } return 0;}

 

转载于:https://www.cnblogs.com/fuzhihong0917/p/9661200.html

你可能感兴趣的文章
unbtu使用笔记
查看>>
OEA 中 WPF 树型表格虚拟化设计方案
查看>>
Android程序开发初级教程(一) 开始 Hello Android
查看>>
使用Gradle打RPM包
查看>>
“我意识到”的意义
查看>>
淘宝天猫上新辅助工具-新品填表
查看>>
再学 GDI+[43]: 文本输出 - 获取已安装的字体列表
查看>>
nginx反向代理
查看>>
操作系统真实的虚拟内存是什么样的(一)
查看>>
hadoop、hbase、zookeeper集群搭建
查看>>
python中一切皆对象------类的基础(五)
查看>>
modprobe
查看>>
android中用ExpandableListView实现三级扩展列表
查看>>
%Error opening tftp://255.255.255.255/cisconet.cfg
查看>>
java读取excel、txt 文件内容,传到、显示到另一个页面的文本框里面。
查看>>
《从零开始学Swift》学习笔记(Day 51)——扩展构造函数
查看>>
python多线程队列安全
查看>>
[汇编语言学习笔记][第四章第一个程序的编写]
查看>>
android 打开各种文件(setDataAndType)转:
查看>>
补交:最最原始的第一次作业(当时没有选上课,所以不知道)
查看>>