题目链接:

闲扯:

终于在集训中敲出正解(虽然与正解不完全相同),开心QAQ

首先比较巧,这题是\(Ebola\)出的一场模拟赛的一道题的树上强化版,当时还口胡出了那题的题解

然而考场上只得了86最后一个substask被卡了,一开始以为毒瘤出题人卡常(虽然真卡了)卡线段树,题目时限1.5s,评测机上两个点擦线1500ms左右,剩下两个点不知道。然后本地测一下都是1900+ms!机子性能已经这样了吗....结果把快读换成\(fread\),TM过了!最慢的1200+ms!!!这......无话可说,\(getchar()\)快读也卡讲究

分析:

首先最简单的处理不讲了.就是把每个点的未知数表示成\(k_i x_1 + b_i\)的形式,这DFS一遍就好了

然后观察到有一个1e3的子任务,想想暴力怎么做,我们对于操作1,相当于\((k_i+k_j)x_1+(b_i+b_j)=w\)判断一下解得情况就好了,\(O(1)\)完成;

对于操作2,我们可以发现对于\(x\)的操作,只会对\(x\)的子树中的\(k_ix_1+b_i\)形式有影响(实际上只会影响\(b_i\)),于是\(DFS\)一遍子树即可,这样总的暴力时间复杂度是\(O(nq)\)

考虑优化暴力,

我们发现瓶颈是操作2,如果将\(x\)与其父亲的边权从\(w_1\)改为\(w_2\),那么加入\(x\)本来形式是\(k_ix_1+b_i\),这时候变成了\(k_i x_1+b_i+w_2-w_1\),相当于加操作,当时在\(x\)的子树中与\(x\)的\(k_i\)(实际上显然只有-1,1两种取值)不同的点,\(b\)值却应该减去\(w_2-w_1\),所以我们将标记开成一个二元组,一个记录标记的正负,另一个记录值,重载下运算符就很方便了

struct Tag{
int o;//标记的正负
ll dt;
Tag(){o=dt=0;}
Tag(int o){o=dt=o;}
Tag(int _o,ll _dt){o=_o,dt=_dt;}
Tag operator +(const Tag &b)const{
Tag tmp=*this;
if(tmp.o==0)tmp=b;
else if(b.o==0)return tmp;
else {
if(o!=b.o){
tmp.dt=dt-b.dt;
}
else tmp.dt=dt+b.dt;
}
return tmp;
}
};

这样对于操作2,只用在子树加个标记就好了,因为dfs序是一段连续区间(我比较傻考场上是用树链剖分)使用线段树就好了

对于操作1,我们两次单点查询就好了,然后按暴力那样处理.

总的时间复杂度\(O(q log N)\),常数稍大

当然标算std是将深度分奇偶考虑,然后树状数组维护差分标记,时间复杂度相同但是常数小的多

代码

这是考场代码换了快读,如果想看线段树部分直接跳到\(niconicoi\)那个\(namespace\)就好了

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
#include <cmath>
#include <vector>
#define ll long long
#define ri register int
using std::min;
using std::abs;
using std::max;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while(!isdigit(c=nc()))ne=c=='-';
x=c-48;
while(isdigit(c=nc()))x=(x<<3)+(x<<1)+c-48;
x=ne?-x:x;return ;
}
const int maxn=100005;
const int inf=0x7fffffff;
const int N=1000005;
struct Edge{
int ne,to;
ll dis;
}edge[N<<1];
int h[N],num_edge=1;
inline void add_edge(int f,int to,int c){
edge[++num_edge].ne=h[f];
edge[num_edge].to=to;
edge[num_edge].dis=c;
h[f]=num_edge;
}
struct Wt{
int ki;
ll bi;
Wt(){ki=bi=0;}
Wt(int _k,ll _b){ki=_k,bi=_b;}
}pt[N];
int n,q;
int fafa[N],fa_id[N];
namespace wtf{
void main(){
/*orz*/
return ;
}
}
void pre_dfs(int now,int fa){
int v;
int x=pt[now].ki,y=pt[now].bi;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa)continue;
fafa[v]=now;
fa_id[v]=i;
pt[v]=Wt(-x,edge[i].dis-y);
pre_dfs(v,now);
}
return;
}
namespace qwq{
void main(){
int op,x,y;ll dd;
ll p=edge[2].dis;
while(q--){
read(op),read(x),read(y);
if(op==1){
read(dd);
if(x!=y){
if(dd==p){
puts("inf");
}
else{
puts("none");
}
}
else {
if(x==1){
if(dd%2)puts("none");
else printf("%lld\n",dd/2);
}
if(x==2){
ll tt=2*p-dd;
if(tt%2)puts("none");
else printf("%lld\n",tt/2);
}
}
}
else{
p=y;
}
}
return ;
}
}
namespace task_1{
void main(){
int op,x,y;ll dd;
int kk,bb;
while(q--){
read(op),read(x),read(y);
if(op==1){
read(dd);
kk=pt[x].ki+pt[y].ki;
bb=pt[x].bi+pt[y].bi;
dd=dd-bb;
if(kk==0){
if(dd==0)puts("inf");
else puts("none");
}
else if(dd%abs(kk)!=0)puts("none");
else printf("%lld\n",dd/kk);
}
else {
edge[fa_id[x]].dis=y;
edge[fa_id[x]^1].dis=y;
pre_dfs(fafa[x],fafa[fafa[x]]);
}
}
return ;
}
}
namespace niconiconi{
int dep[N],top[N],son[N],size[N],dfn[N],rnk[N],tot=0;
void print(ll xxx){
if(!xxx)return ;
print(xxx/10);
putchar(xxx%10+'0');
return ;
}
void dfs_1(int now){
int v;size[now]=1;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fafa[now])continue;
dep[v]=dep[now]+1;
dfs_1(v);
size[now]+=size[v];
if(!son[now]||size[son[now]]<size[v])son[now]=v;
}
return ;
}
void dfs_2(int now,int t){
int v;top[now]=t;
dfn[now]=++tot,rnk[tot]=now;
if(!son[now])return ;
dfs_2(son[now],t);
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fafa[now]||v==son[now])continue;
dfs_2(v,v);
}
return ;
}
struct Tag{
int o;//标记的正负
ll dt;
Tag(){o=dt=0;}
Tag(int o){o=dt=o;}
Tag(int _o,ll _dt){o=_o,dt=_dt;}
Tag operator +(const Tag &b)const{
Tag tmp=*this;
if(tmp.o==0)tmp=b;
else if(b.o==0)return tmp;
else {
if(o!=b.o){
tmp.dt=dt-b.dt;
}
else tmp.dt=dt+b.dt;
}
return tmp;
}
};
Tag tag[N<<2];
void build(int now,int l,int r){
tag[now]=Tag(0);
if(l==r){
return ;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
return ;
}
int L,R;
Tag dta;
inline void pushdown(int now){
if(tag[now].o==0)return ;
tag[now<<1]=tag[now<<1]+tag[now];
tag[now<<1|1]=tag[now<<1|1]+tag[now];
tag[now]=Tag(0);
return ;
}
void update(int now,int l,int r){
if(L<=l&&r<=R){
tag[now]=tag[now]+dta;
return ;
}
int mid=(l+r)>>1;
pushdown(now);
if(L<=mid)update(now<<1,l,mid);
if(mid<R)update(now<<1|1,mid+1,r);
return ;
}
Wt pa,pb;
int t;
void query(int now,int l,int r){
if(l==r){
//int kkk=pt[rnk[l]].ki,bbb=pt[rnk[l]].bi;
if(tag[now].o!=0){
if(tag[now].o!=pt[rnk[l]].ki){
pt[rnk[l]].bi-=tag[now].dt;
}
else{
pt[rnk[l]].bi+=tag[now].dt;
}
tag[now]=Tag(0);
}
//pa.ki=pt[rnk[l]].ki;
//pa.bi=pt[rnk[l]].bi;
return;
}
int mid=(l+r)>>1;
pushdown(now);
if(t<=mid)query(now<<1,l,mid);
else query(now<<1|1,mid+1,r);
return ;
}
void main(){
int op,x,y;
ll kk,bb,dd;
dep[1]=0;
dfs_1(1);
dfs_2(1,1);
build(1,1,n);
while(q--){
read(op),read(x),read(y);
if(op==1){
read(dd);
t=dfn[x];//pa=Wt(0,0);
query(1,1,n);
t=dfn[y];//pb=Wt(pa.ki,pa.bi),pa=Wt(0,0);
query(1,1,n);
//printf("%d %d %d %d\n",pa.ki,pb.ki,pa.bi,pb.bi);
kk=pt[x].ki+pt[y].ki;
bb=pt[x].bi+pt[y].bi;
dd=dd-bb;
if(kk==0){
if(dd==0)puts("inf");
else puts("none");
}
else if(dd%abs(kk)!=0)puts("none");
else {
if(dd==0)puts("0");
else {
dd=dd/kk;
if(dd<0)dd=-dd,putchar('-');
print(dd);
puts("");
}
//printf("%lld\n",dd/kk);
}
}
else {
dd=edge[fa_id[x]].dis;
edge[fa_id[x]].dis=edge[fa_id[x]^1].dis=y;
dd=1ll*y-dd;
//printf("%lld\n",dd);
L=dfn[x],R=dfn[x]+size[x]-1;
//t=dfn[x];pa=Wt(0,0);
//query(1,1,n);
pa=pt[x];
//printf("%d\n",pa.ki);
dta=Tag(pa.ki,dd);
update(1,1,n);
//update_subtree()
}
}
return ;
}
}
int main(){
int x,y;
freopen("equation.in","r",stdin);
freopen("equation.out","w",stdout);
read(n),read(q);
for(ri i=2;i<=n;i++){
read(x),read(y);
add_edge(i,x,y);
add_edge(x,i,y);
}
pt[1].ki=1,pt[1].bi=0;
fafa[1]=0;
pre_dfs(1,0);
if(q==0)wtf::main();
else if(n==2)qwq::main();
else if(n<=2000)task_1::main();
else niconiconi::main();
fclose(stdin);
fclose(stdout);
return 0;
}

[NOIP10.6模拟赛]2.equation题解--DFS序+线段树的更多相关文章

  1. BZOJ 3252题解(贪心+dfs序+线段树)

    题面 传送门 分析 此题做法很多,树形DP,DFS序+线段树,树链剖分都可以做 这里给出DFS序+线段树的代码 我们用线段树维护到根节点路径上节点权值之和的最大值,以及取到最大值的节点编号x 每次从根 ...

  2. 【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树

    题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\), ...

  3. BZOJ1103 [POI2007]大都市meg dfs序 线段树

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1103 题意概括 一棵树上,一开始所有的边权值为1,我们要支持两种操作: 1. 修改某一条边的权值为 ...

  4. CodeForces 877E Danil and a Part-time Job(dfs序+线段树)

    Danil decided to earn some money, so he had found a part-time job. The interview have went well, so ...

  5. 洛谷P3178 [HAOI2015]树上操作(dfs序+线段树)

    P3178 [HAOI2015]树上操作 题目链接:https://www.luogu.org/problemnew/show/P3178 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边 ...

  6. CodeForces 877E DFS序+线段树

    CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...

  7. [51nod 1681]公共祖先(dfs序+线段树合并)

    [51nod 1681]公共祖先(dfs序+线段树合并) 题面 给出两棵n(n<=100000)个点的树,对于所有点对求它们在两棵树中公共的公共祖先数量之和. 如图,对于点对(2,4),它们在第 ...

  8. 牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树)

    牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树) 链接:https://ac.nowcoder.com/acm/problem/15706 现在需要您来帮忙维护这个名册, ...

  9. codevs1228 (dfs序+线段树)

    1228 苹果树  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题目描述 Description 在卡卡的房子外面,有一棵苹果树.每年的春天,树上总会结 ...

随机推荐

  1. Facebook币Libra学习-6.发行属于自己的代币Token案例(含源码)

    在这个简短的概述中,我们描述了我们在eToro标记化资产背后实施技术的初步经验,即MoveIR语言中的(eToken),用于在Libra网络上进行部署. Libra协议是一个确定性状态机,它将数据存储 ...

  2. selenium历史版本下载

    python历史版本下载 https://pypi.org/project/selenium/#history java历史版本下载  http://selenium-release.storage. ...

  3. Java NIO学习笔记八 Pipe

    Java NIO Pipe Java NIO管道是两个线程之间的单向数据连接.Pipe 具有源信道和接受通道.您将数据写入sink通道.然后可以从源通道读取该数据. 这是一个原理的Pipe流程图: J ...

  4. [C++]单源最短路径:迪杰斯特拉(Dijkstra)算法(贪心算法)

    1 Dijkstra算法 1.1 算法基本信息 解决问题/提出背景 单源最短路径(在带权有向图中,求从某顶点到其余各顶点的最短路径) 算法思想 贪心算法 按路径长度递增的次序,依次产生最短路径的算法 ...

  5. node.js运行内存堆溢出的解决办法

    我是在将一组80多列13万多行的数据通过node-xlsx的时候出现的内存堆溢出的情况. 解决办法时将: node app.js 改成: node --max_old_space_size=10000 ...

  6. 【转载】CentOS7下使用LVM给系统硬盘扩容

    原文地址:https://www.cnblogs.com/ding2016/p/9680690.html 简单介绍: LVM是逻辑盘卷管理(Logical Volume Manager)的简称,它是L ...

  7. selenium+python自动化框架

    流程: 环境准备 eclipse :需安装pydev.testng插件 python :安装python完成后,需 pip下安装selenium:命令: pip install selenium 我现 ...

  8. windows下libnet ARP

    查找自己的网卡: #include <libnet.h> #include <stdio.h> #include <iostream> #pragma commen ...

  9. C语言控制台软件制作

    本题要求你写个程序把给定的符号打印成沙漏的形状.例如给定17个“*”,要求按下列格式打印 ***** *** * *** ***** 所谓“沙漏形状”,是指每行输出奇数个符号:各行符号中心对齐:相邻两 ...

  10. python进程池 使用Queue实现计数功能

    多进程中各个进程间相互隔离,进程间通信需要使用到通道. 多进程中使用Queue实现进程中通信 from multiprocessing import Process,Queue import time ...