BZOJ 3159: 决战

1 sec 512MB

题意:

给你一颗\(n\)个点,初始点权为\(0\)的有跟树,要求支持

  • Increase x y w 将路径\(x\)到\(y\)所有点点权加上\(w\)
  • Sum x y 询问路径\(x\)到\(y\)的点权和
  • Major x y 询问从路径\(x\)到\(y\)最大点权
  • Minor x y 询问最小点权
  • Invert x y 将路径上的点权翻转

有个性质,修改操作一定满足\(x\)为\(y\)祖先或者\(y\)为\(x\)祖先(实际没什么用处)

\(n\le 50000,|w|\le 1000\)

输入格式

第一行有三个整数\(N\)、\(M\)和\(R\),分别表示树的节点数、指令和询问总数,以及树的跟。

接下来\(N-1\)行,每行两个整数\(u\)和\(v\),表示一条边。

接下来\(M\)行,每行描述一个指令或询问,格式见题意描述。

输出格式

对于每个询问操作,输出所求的值。


老年菜鸡选手实在写不动大数据结构题...

有两个做法,暂时只写了一种,有可能一会儿会把另一种补充一下。

考虑树链剖分,但是不能维护翻转

考虑平衡树可以维护翻转

于是我们可以拿平衡树维护树剖的每条链

然后每次修改只有\(\log\)条链,我们把每条链要修改的地方拎出来,然后塞到一个平衡树里面,打一个翻转tag,再塞回去就可以了

用fhqtreap实现起来比较方便

说起来蛮简单,写起来还是蛮难受的的


Code:

#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#define ll long long
const int SIZE=1<<21;
char ibuf[SIZE],*iS,*iT;
//#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
#define gc() getchar()
template <class T>
void read(T &x)
{
int f=0;x=0;char c=gc();
while(!isdigit(c)) f|=c=='-',c=gc();
while(isdigit(c)) x=x*10+c-'0',c=gc();
if(f) x=-x;
}
void reads(char *s)
{
int k=0;char c=gc();
while(c<'A'||c>'Z') c=gc();
while((c>='a'&&c<='z')||(c>='A'&&c<='Z')) s[k++]=c,c=gc();
}
const int N=5e4+10;
inline void ckmax(int &x,int y){x=x>y?x:y;}
inline void ckmin(int &x,int y){x=x<y?x:y;}
int n,m,r,root[N];
namespace treap
{
int ch[N][2],mx[N],mi[N],siz[N],val[N],dat[N],tag[N],reve[N],tot;
ll sum[N];
#define ls ch[now][0]
#define rs ch[now][1]
void updata(int now)
{
sum[now]=sum[ls]+dat[now]+sum[rs];
siz[now]=siz[ls]+1+siz[rs];
mx[now]=mi[now]=dat[now];
if(ls)
{
ckmax(mx[now],mx[ls]);
ckmin(mi[now],mi[ls]);
}
if(rs)
{
ckmax(mx[now],mx[rs]);
ckmin(mi[now],mi[rs]);
}
}
void Reverse(int now)
{
std::swap(ls,rs),reve[now]^=1;
}
void upt(int now,int d)
{
dat[now]+=d;
tag[now]+=d;
mi[now]+=d;
mx[now]+=d;
sum[now]+=d*siz[now];
}
void pushdown(int now)
{
if(reve[now])
{
if(ls) Reverse(ls);
if(rs) Reverse(rs);
reve[now]=0;
}
if(tag[now])
{
if(ls) upt(ls,tag[now]);
if(rs) upt(rs,tag[now]);
tag[now]=0;
}
}
void split(int now,int &x,int &y,int k)
{
if(!now){x=y=0;return;}
pushdown(now);
if(k<=siz[ls])
y=now,split(ls,x,ch[y][0],k);
else
x=now,split(rs,ch[x][1],y,k-siz[ls]-1);
updata(now);
}
int Merge(int x,int y)
{
if(!x||!y) return x^y;
pushdown(x),pushdown(y);
if(val[x]<val[y])
{
ch[x][1]=Merge(ch[x][1],y);
updata(x);
return x;
}
else
{
ch[y][0]=Merge(x,ch[y][0]);
updata(y);
return y;
}
}
int New(int d)
{
val[++tot]=rand(),siz[tot]=1,sum[tot]=mi[tot]=mx[tot]=dat[tot]=d;
return tot;
}
void ins(int id,int d)
{
root[id]=Merge(root[id],New(d));
}
ll querysum(int id,int l,int r)
{
int x,y,z;
split(root[id],x,y,r);
split(x,x,z,l-1);
ll ret=sum[z];
root[id]=Merge(x,Merge(z,y));
return ret;
}
int querymi(int id,int l,int r)
{
int x,y,z;
split(root[id],x,y,r);
split(x,x,z,l-1);
int ret=mi[z];
root[id]=Merge(x,Merge(z,y));
return ret;
}
int querymx(int id,int l,int r)
{
int x,y,z;
split(root[id],x,y,r);
split(x,x,z,l-1);
int ret=mx[z];
root[id]=Merge(x,Merge(z,y));
return ret;
}
void modify(int id,int l,int r,int w)
{
int x,y,z;
split(root[id],x,y,r);
split(x,x,z,l-1);
upt(z,w);
root[id]=Merge(x,Merge(z,y));
}
}
using treap::modify;
using treap::querysum;
using treap::querymi;
using treap::querymx;
using treap::ins;
using treap::Merge;
using treap::split;
using treap::Reverse;
int head[N],to[N<<1],Next[N<<1],cnt;
void add(int u,int v)
{
to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
}
int top[N],ws[N],siz[N],par[N],dep[N],rk[N],num[N];
void dfs(int now)
{
siz[now]=1;
dep[now]=dep[par[now]]+1;
for(int v,i=head[now];i;i=Next[i])
if((v=to[i])!=par[now])
{
par[v]=now;
dfs(v);
siz[now]+=siz[v];
if(siz[ws[now]]<siz[v]) ws[now]=v;
}
}
int lin,tot[N];
void dfs(int now,int id,int anc)
{
top[now]=anc;
num[now]=id;
rk[now]=++tot[id];
ins(id,0);
if(ws[now]) dfs(ws[now],id,anc);
for(int v,i=head[now];i;i=Next[i])
if(!num[v=to[i]])
dfs(v,++lin,v);
}
void modi(int x,int y,int w)
{
if(dep[x]<dep[y]) std::swap(x,y);
while(top[x]!=top[y])
{
modify(num[x],1,rk[x],w);
x=par[top[x]];
}
if(dep[x]<dep[y]) std::swap(x,y);
modify(num[x],rk[y],rk[x],w);
}
void qrysum(int x,int y)
{
ll ret=0;
while(top[x]!=top[y])
{
if(dep[top[x]]>dep[top[y]])
{
ret+=querysum(num[x],1,rk[x]);
x=par[top[x]];
}
else
{
ret+=querysum(num[y],1,rk[y]);
y=par[top[y]];
}
}
if(dep[x]<dep[y]) std::swap(x,y);
ret+=querysum(num[x],rk[y],rk[x]);
printf("%lld\n",ret);
}
void qrymx(int x,int y)
{
int ret=-(1<<30);
while(top[x]!=top[y])
{
if(dep[top[x]]>dep[top[y]])
{
ckmax(ret,querymx(num[x],1,rk[x]));
x=par[top[x]];
}
else
{
ckmax(ret,querymx(num[y],1,rk[y]));
y=par[top[y]];
}
}
if(dep[x]<dep[y]) std::swap(x,y);
ckmax(ret,querymx(num[x],rk[y],rk[x]));
printf("%d\n",ret);
}
void qrymi(int x,int y)
{
int ret=1<<30;
while(top[x]!=top[y])
{
if(dep[top[x]]>dep[top[y]])
{
ckmin(ret,querymi(num[x],1,rk[x]));
x=par[top[x]];
}
else
{
ckmin(ret,querymi(num[y],1,rk[y]));
y=par[top[y]];
}
}
if(dep[x]<dep[y]) std::swap(x,y);
ckmin(ret,querymi(num[x],rk[y],rk[x]));
printf("%d\n",ret);
}
void rev(int x,int y)
{
int tx=x,ty=y;
if(dep[x]<dep[y]) std::swap(x,y);
int rt=0;
while(top[x]!=top[y])
{
int id=num[x],k=rk[x],pre;
split(root[id],pre,root[id],k);
rt=Merge(pre,rt);
x=par[top[x]];
}
if(dep[x]<dep[y]) std::swap(x,y);
int id=num[x],l=rk[y],r=rk[x],a,b,c;
split(root[id],a,c,r);
split(a,a,b,l-1);
rt=Merge(b,rt);
Reverse(rt);
x=tx,y=ty;
if(dep[x]<dep[y]) std::swap(x,y);
while(top[x]!=top[y])
{
int id=num[x],k=rk[x],pre;
split(rt,rt,pre,treap::siz[rt]-k);
root[id]=Merge(pre,root[id]);
x=par[top[x]];
}
id=num[x];
root[id]=Merge(a,Merge(rt,c));
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
read(n),read(m),read(r);
for(int u,v,i=1;i<n;i++) read(u),read(v),add(u,v),add(v,u);
dfs(r);
dfs(r,++lin,r);
char op[23];
for(int x,y,w,i=1;i<=m;i++)
{
reads(op),read(x),read(y);
if(op[0]=='I')
{
if(op[2]=='c') read(w),modi(x,y,w);
else rev(x,y);
}
else if(op[0]=='S') qrysum(x,y);
else
{
if(op[1]=='a') qrymx(x,y);
else qrymi(x,y);
}
}
return 0;
}

2019.5.22

BZOJ 3159: 决战 解题报告的更多相关文章

  1. BZOJ 3159决战

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3159 题意:给出一棵树,(1)路径加一个值:(2)路径上的节点的值反转(只是值反转,不是节 ...

  2. POJ 3159 Candies 解题报告(差分约束 Dijkstra+优先队列 SPFA+栈)

    原题地址:http://poj.org/problem?id=3159 题意大概是班长发糖果,班里面有不良风气,A希望B的糖果不比自己多C个.班长要满足小朋友的需求,而且要让自己的糖果比snoopy的 ...

  3. BZOJ 2959: 长跑 解题报告

    2959: 长跑 Description 某校开展了同学们喜闻乐见的阳光长跑活动.为了能"为祖国健康工作五十年",同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑 ...

  4. BZOJ 4238 电压 解题报告

    BZOJ 4238 电压 考虑一条边成为答案以后,删去Ta后剩下的图是一个或很多个二分图,即没有奇环 则一条边可以成为答案,当且仅当自己在所有奇环的交上且不在偶环上. 考虑建出dfs树,那么返祖边一定 ...

  5. BZOJ 3328: PYXFIB 解题报告

    BZOJ 3328: PYXFIB 题意 给定\(n,p,k(1\le n\le 10^{18},1\le k\le 20000,1\le p\le 10^9,p \ is \ prime,k|(p- ...

  6. BZOJ 4302 Buildings 解题报告

    这个题好像很有趣的样子. 题目分析: 房间都是 $1\times k$ 的,也就是一条一条的.这个好像比较显然的样子. 一个房间如果要覆盖某个格子$u$,那么这个房间的面积至少为 $dis(u, Bo ...

  7. BZOJ 3160 万径人踪灭 解题报告

    这个题感觉很神呀.将 FFT 和 Manacher 有机结合在了一起. 首先我们不管那个 “不能连续” 的条件,那么我们就可以求出有多少对字母关于某一条直线对称,然后记 $T_i$ 为关于直线 $i$ ...

  8. BZOJ 4127 Abs 解题报告

    这个题感觉很厉害的样子.. 首先我们注意到一点:每次加的 $d$ 都是非负的. 那么就说明一个数只可能从负数变成非负数并且只会变一次. 所以我们就可以暴力地去改变一个数的正负情况. 然后我们就可以用树 ...

  9. BZOJ 3953 Self-Assembly 解题报告

    首先,我们可以先考虑一个暴力一点的算法: 对于任意两个分子,如果它们能以至少一种进行匹配,那么我们就在这两个分子之间连一条边. 然后如果我们能找到一个环,就说明是 unbounded,否则就是 bou ...

随机推荐

  1. spring Data solr依赖文件 和xml配置文件

    <dependencies>        <dependency>            <groupId>org.springframework.data< ...

  2. Steup Factory 操作注册表

    //判断注册表是否存在,不存在就创建 result = Registry.DoesKeyExist(HKEY_CURRENT_USER, "SOFTWARE\\MyTestApp" ...

  3. loadRunner之参数关联

    录制脚本,对用户名和密码进行参数化: Action() { web_url("WebTours", "URL=http://127.0.0.1:1080/WebTours ...

  4. java html生成PDF,并打印

    import java.io.File; import java.io.FileOutputStream; import org.zefer.pd4ml.PD4Constants; import or ...

  5. 【LeetCode 31】下一个排列

    题目链接 [题解] 从右往左找第一个下降的位置i(即满足nums[i]<nums[i+1]); 然后在[i+1..len-1]这个区间里面找到一个最大的下标k,使得nums[k]>nums ...

  6. TP model where条件丢失

    最近我修复了一个bug,这个bug是用户能看到所有用户的数据,经过排查发现是where条件丢失,导致查询语句直接查了所有数据. 但是代码并没有问题,然后查到了 ThinkPHP/Library/Thi ...

  7. paper 143:人脸验证

    持续更新ing,敬请期待! 参考:http://blog.csdn.net/stdcoutzyx/article/details/42091205  1. DeepID人脸识别算法 香港中文大学的团队 ...

  8. dotnet core项目的nuget存储路径

    Where's the package location in aspnet core For project.json the nuget directory is in the user prof ...

  9. MySQL strcmp 函数

    STRCMP(str1, str2) 比较两个字符串,如果这两个字符串相等返回0,如果第一个参数是根据当前的排序小于第二个参数顺序返回-1,否则返回1. mysql> SELECT STRCMP ...

  10. SQLSERVER 时间日期函数,查询今天日期、昨天、一个星期、半年前的数据

    今天的所有数据:select * from 表名 where DateDiff(dd,datetime类型字段,getdate())=0昨天的所有数据:select * from 表名 where D ...