某两个点间的请求只对不在这条路径上的询问有影响。那么容易想到每次修改除该路径上的所有点的答案。对每个点建个两个堆,其中一个用来删除,线段树维护即可。由于一条路径在树剖后的dfs序中是log个区间,所以其补集也是log个区间。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 100010
int n,m,p[N],t,cnt;
int id[N],tag[N],top[N],fa[N],deep[N],size[N],son[N];
int L[N<<],R[N<<];
struct data{int to,nxt;
}edge[N<<];
struct data2{int x,y,z;
}q[N<<];
struct data3
{
int l,r;
bool operator <(const data3&a) const
{
return r<a.r;
}
}a[N];
struct heap
{
priority_queue<int> a,b;
void check(){while (!b.empty()&&!a.empty()&&a.top()==b.top()) a.pop(),b.pop();}
void ins(int x){a.push(x);check();}
void del(int x){b.push(x);check();}
}tree[N<<];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void dfs1(int k)
{
size[k]=;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k])
{
deep[edge[i].to]=deep[k]+;
fa[edge[i].to]=k;
dfs1(edge[i].to);
size[k]+=size[edge[i].to];
if (size[son[k]]<size[edge[i].to]) son[k]=edge[i].to;
}
}
void dfs2(int k,int from)
{
top[k]=from;
id[k]=++cnt;tag[cnt]=k;
if (son[k]) dfs2(son[k],from);
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k]&&edge[i].to!=son[k])
dfs2(edge[i].to,edge[i].to);
}
void build(int k,int l,int r)
{
L[k]=l,R[k]=r;tree[k].ins(-);
if (l==r) return;
int mid=l+r>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
}
void down(int k)
{
while (!tree[k].a.empty())
{
int x=tree[k].a.top();tree[k].a.pop();
tree[k<<].ins(x);tree[k<<|].ins(x);
tree[k].check();
}
while (!tree[k].b.empty())
{
int x=tree[k].b.top();tree[k].b.pop();
tree[k<<].del(x);tree[k<<|].del(x);
}
}
void ins(int k,int l,int r,int x)
{
if (L[k]==l&&R[k]==r) {tree[k].ins(x);return;}
down(k);
int mid=L[k]+R[k]>>;
if (r<=mid) ins(k<<,l,r,x);
else if (l>mid) ins(k<<|,l,r,x);
else ins(k<<,l,mid,x),ins(k<<|,mid+,r,x);
}
void del(int k,int l,int r,int x)
{
if (L[k]==l&&R[k]==r) {tree[k].del(x);return;}
down(k);
int mid=L[k]+R[k]>>;
if (r<=mid) del(k<<,l,r,x);
else if (l>mid) del(k<<|,l,r,x);
else del(k<<,l,mid,x),del(k<<|,mid+,r,x);
}
int query(int k,int x)
{
if (L[k]==R[k]) return tree[k].a.top();
down(k);
int mid=L[k]+R[k]>>;
if (x<=mid) return query(k<<,x);
else return query(k<<|,x);
}
void modify(int x,int y,int z)
{
int m=;
while (top[x]!=top[y])
{
if (deep[top[x]]<deep[top[y]]) swap(x,y);
m++,a[m].l=id[top[x]],a[m].r=id[x];
x=fa[top[x]];
}
if (deep[x]<deep[y]) swap(x,y);
m++,a[m].l=id[y],a[m].r=id[x];
sort(a+,a+m+);
a[].l=a[].r=,a[m+].l=a[m+].r=n+;
for (int i=;i<=m;i++)
if (a[i].r+<=a[i+].l-)
if (z>) ins(,a[i].r+,a[i+].l-,z);
else del(,a[i].r+,a[i+].l-,-z);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj4538.in","r",stdin);
freopen("bzoj4538.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();
for (int i=;i<n;i++)
{
int x=read(),y=read();
addedge(x,y),addedge(y,x);
}
dfs1();
dfs2(,);
build(,,n);
for (int i=;i<=m;i++)
{
int op=read();
switch(op)
{
case :
{
q[i].x=read(),q[i].y=read(),q[i].z=read();
modify(q[i].x,q[i].y,q[i].z);
break;
}
case :
{
int x=read();
modify(q[x].x,q[x].y,-q[x].z);
break;
}
case :
{
int x=read();
printf("%d\n",query(,id[x]));
}
}
}
return ;
}

  然而由于复杂度是O(nlog3n)的以及蒟蒻自带大常数,在luogu上T了两个点,bzoj甚至直接MLE了。于是考虑有没有更好的做法。

  感觉上修改时对于某一条路径之外的都要修改过于暴力,考虑是否能改成修改这条路径。如果只有一次询问,可以二分答案,统计出有多少个修改的权值不小于该答案,并统计上述种类的修改覆盖了查询点多少次,若两者相等则说明答案不可行,否则可行。修改路径可以通过树上差分维护子树和完成。

  现在有多组询问,可以想到整体二分。对整体二分一个答案,按时间顺序依次修改、查询,每次做完之后都缩小了每一个询问的答案范围,将询问按照答案是否大于mid、修改按照权值是否大于mid扔在两边即可。于是复杂度O(nlog2n)。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 100010
#define inf 1000000000
int n,m,p[N],t,tree[N],id[N],size[N],fa[N][],deep[N],cnt;
struct data{int to,nxt;
}edge[N<<];
struct data2{int op,x,y,z,l,i,ans;
}q[N<<],tmp[N<<];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
bool cmp(const data2&a,const data2&b)
{
return a.i<b.i;
}
void dfs(int k,int from)
{
id[k]=++cnt;size[k]=;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from)
{
deep[edge[i].to]=deep[k]+;
fa[edge[i].to][]=k;
dfs(edge[i].to,k);
size[k]+=size[edge[i].to];
}
}
int lca(int x,int y)
{
if (deep[x]<deep[y]) swap(x,y);
for (int j=;~j;j--) if (deep[fa[x][j]]>=deep[y]) x=fa[x][j];
if (x==y) return x;
for (int j=;~j;j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
return fa[x][];
}
void add(int k,int x){while (k<=n){tree[k]+=x;k+=k&-k;}}
int sum(int k){int s=;while (k){s+=tree[k];k-=k&-k;}return s;}
void solve(int l,int r,int low,int high)
{
if (l>r) return;
if (low==high)
{
for (int i=l;i<=r;i++)
if (q[i].op==) q[i].ans=low;
return;
}
int mid=low+high>>,s=;
for (int i=l;i<=r;i++)
if (q[i].op!=)
{
if (q[i].z>mid)
{
int t=q[i].op==?:-;
s+=t;
add(id[q[i].x],t),add(id[q[i].y],t);
add(id[q[i].l],-t);
if (q[i].l>) add(id[fa[q[i].l][]],-t);
}
}
else if (sum(id[q[i].x]+size[q[i].x]-)-sum(id[q[i].x]-)==s) q[i].ans=low;
else q[i].ans=high;
int cut=l;
for (int i=l;i<=r;i++)
if (q[i].op==&&q[i].ans<=mid||q[i].op!=&&q[i].z<=mid) tmp[cut++]=q[i];
int t=cut;
for (int i=l;i<=r;i++)
if (q[i].op==&&q[i].ans>mid||q[i].op!=&&q[i].z>mid) tmp[t++]=q[i];
for (int i=l;i<=r;i++) q[i]=tmp[i];
for (int i=cut;i<=r;i++)
if (q[i].op!=)
{
int t=q[i].op==?-:;
add(id[q[i].x],t),add(id[q[i].y],t);
add(id[q[i].l],-t);
if (q[i].l>) add(id[fa[q[i].l][]],-t);
}
solve(l,cut-,low,mid);
solve(cut,r,mid+,high);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj4538.in","r",stdin);
freopen("bzoj4538.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();
for (int i=;i<n;i++)
{
int x=read(),y=read();
addedge(x,y),addedge(y,x);
}
fa[][]=;dfs(,);
for (int j=;j<;j++)
for (int i=;i<=n;i++)
fa[i][j]=fa[fa[i][j-]][j-];
for (int i=;i<=m;i++)
{
q[i].op=read();q[i].i=i;
switch(q[i].op)
{
case :
{
q[i].x=read(),q[i].y=read(),q[i].z=read(),q[i].l=lca(q[i].x,q[i].y);
break;
}
case :
{
int x=read();
q[i].x=q[x].x,q[i].y=q[x].y,q[i].z=q[x].z,q[i].l=q[x].l;
break;
}
case :q[i].x=read(),q[i].ans=-;
}
}
solve(,m,-,inf);
sort(q+,q+m+,cmp);
for (int i=;i<=m;i++)
if (q[i].op==) printf("%d\n",q[i].ans);
return ;
}

BZOJ4538 HNOI2016网络(树链剖分+线段树+堆/整体二分+树上差分)的更多相关文章

  1. ACM-ICPC 2018 焦作赛区网络预赛 E Jiu Yuan Wants to Eat (树链剖分+线段树)

    题目链接:https://nanti.jisuanke.com/t/31714 题意:给你一棵树,初始全为0,有四种操作: 1.u-v乘x    2.u-v加x   3. u-v取反  4.询问u-v ...

  2. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  3. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  4. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  5. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  6. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  7. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  8. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

  9. 【POJ3237】Tree(树链剖分+线段树)

    Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...

  10. HDU 2460 Network(双连通+树链剖分+线段树)

    HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...

随机推荐

  1. GZIP压缩提高网络传输效率

    [spring]通过GZIP压缩提高网络传输效率(可以实现任何资源的gzip压缩.包括AJAX) gzip是http协议中使用的一种加密算法,客户端向web服务器端发出了请求后,通常情况下服务器端会将 ...

  2. Java发送QQ邮件

    面试的时候被问到这个问题,别人问我用Java发过邮件没有,被问得一脸懵逼.然后就研究了一下,不是很难,按照网上的方法折腾了几天就搞出来了. 首先,使用QQ邮箱发送邮件之前需要在邮箱里面配置,开启pop ...

  3. [04] JSP标准动作

    1.概述 JSP规范中定义了一系列的标准动作,Web容器按照规范进行了实现,可以解析并执行标准动作.而标准动作使用的是标准的xml语法,看上去也比较直观易懂,下面来看一个结构例子: <jsp:a ...

  4. ASP.NET Core中获取完整的URL(转载)

    在之前的ASP.NET中,可以通过 Request.Url.AbsoluteUri 获取,但在ASP.NET Core没有这个实现,请问如何获取呢?方法一:先引用“using Microsoft.As ...

  5. npm install xxx --save-dev 与npm install xxx --save 的区别

    正常情况下: 当你为你的模块安装一个依赖模块时 1.你得先安装他们(在模块根目录下npm install module-name) 2.连同版本号手动将他们添加到模块配置文件package.json中 ...

  6. Luogu P3768 简单的数学题

    非常恶心的一道数学题,推式子推到吐血. 光是\(\gcd\)求和我还是会的,但是多了个\(ij\)是什么鬼东西. \[\sum_{i=1}^n\sum_{j=1}^nij\gcd(i,j)=\sum_ ...

  7. $\mathfrak {reputation}$

    \(\mathfrak {reputation}\) 举世盛名 身败名裂

  8. 【下一代核心技术DevOps】:(三)私有代码库阿里云Git使用

    1. 引言 使用DevOps肯定离不开和代码的集成.所以要想跑通整套流程,代码库的选型也是非常重要的.否则无法实现持续集成.目前比较常用的代码管理有SVN和GIt 如果还使用SVN的,建议尽早迁移到G ...

  9. SpringBoot日记——分布式篇

    思考:什么是分布式?什么是微服务? 一些概念:RPC-远程过程调用,某台机器想要调用另一台机器所需要的一种服务,及分布式的服务框架,比如dubbo或者SpringCloud. 铺天盖地的分布式互联网系 ...

  10. 小白必须懂的MongoDB的十大总结

    小白必须懂的MongoDB的总结 一.MongoDB的认识 1.什么是MongoDB? MongoDB 是一个介于关系数据库和非关系数据库之间的开源产品,是最接近于关系型数据库的 NoSQL 数据库. ...