BZOJ 3159: 决战 解题报告
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: 决战 解题报告的更多相关文章
- BZOJ 3159决战
		题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3159 题意:给出一棵树,(1)路径加一个值:(2)路径上的节点的值反转(只是值反转,不是节 ... 
- POJ 3159 Candies 解题报告(差分约束 Dijkstra+优先队列 SPFA+栈)
		原题地址:http://poj.org/problem?id=3159 题意大概是班长发糖果,班里面有不良风气,A希望B的糖果不比自己多C个.班长要满足小朋友的需求,而且要让自己的糖果比snoopy的 ... 
- BZOJ 2959: 长跑 解题报告
		2959: 长跑 Description 某校开展了同学们喜闻乐见的阳光长跑活动.为了能"为祖国健康工作五十年",同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑 ... 
- BZOJ 4238 电压 解题报告
		BZOJ 4238 电压 考虑一条边成为答案以后,删去Ta后剩下的图是一个或很多个二分图,即没有奇环 则一条边可以成为答案,当且仅当自己在所有奇环的交上且不在偶环上. 考虑建出dfs树,那么返祖边一定 ... 
- 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- ... 
- BZOJ 4302 Buildings 解题报告
		这个题好像很有趣的样子. 题目分析: 房间都是 $1\times k$ 的,也就是一条一条的.这个好像比较显然的样子. 一个房间如果要覆盖某个格子$u$,那么这个房间的面积至少为 $dis(u, Bo ... 
- BZOJ 3160 万径人踪灭 解题报告
		这个题感觉很神呀.将 FFT 和 Manacher 有机结合在了一起. 首先我们不管那个 “不能连续” 的条件,那么我们就可以求出有多少对字母关于某一条直线对称,然后记 $T_i$ 为关于直线 $i$ ... 
- BZOJ 4127 Abs 解题报告
		这个题感觉很厉害的样子.. 首先我们注意到一点:每次加的 $d$ 都是非负的. 那么就说明一个数只可能从负数变成非负数并且只会变一次. 所以我们就可以暴力地去改变一个数的正负情况. 然后我们就可以用树 ... 
- BZOJ 3953 Self-Assembly 解题报告
		首先,我们可以先考虑一个暴力一点的算法: 对于任意两个分子,如果它们能以至少一种进行匹配,那么我们就在这两个分子之间连一条边. 然后如果我们能找到一个环,就说明是 unbounded,否则就是 bou ... 
随机推荐
- Hadoop(一)阿里云hadoop集群配置
			集群配置 三台ECS云服务器 配置步骤 1.准备工作 1.1 创建/bigdata目录 mkdir /bigdatacd /bigdatamkdir /app 1.2修改主机名为node01.node ... 
- linux 应用层常用API/命令
			一 API 1. bsearch NAME bsearch - binary search of a sorted array SYNOPSIS #include <stdlib.h> v ... 
- SQL Join连接
			SQL 连接(Joins) SQL join 用于把来自两个或多个表的行结合起来. SQL JOIN SQL JOIN 子句用于把来自两个或多个表的行结合起来,基于这些表之间的共同字段. 最常见的 J ... 
- 「NOI2016」区间 解题报告
			「NOI2016」区间 最近思维好僵硬啊... 一上来就觉得先把区间拆成两个端点进行差分,然后扫描位置序列,在每个位置维护答案,用数据结构维护当前位置的区间序列,但是不会维护. 于是想研究性质,想到为 ... 
- OpenGL 学习总结
			最终呈现画出三角形的一个方式: public void draw(float[] mvpMatrix) { // Add program to OpenGL ES environment GLES20 ... 
- laravel生成key
			安装laravel之后要设置一个随即字符串作为应用密钥(key),如果你是通过composer或者laravel安装器安装的 这个key已经自动生成并设置了. 一般情况下这个key为32位长度字符串. ... 
- @RequestParam 引发的编译问题
			在使用SpringMVC绑定基本类型(如String,Integer等)参数时,应通过@RequestParam注解指定具体的参数名称,否则,当源代码在非debug模式下编译后,运行时会引发Handl ... 
- editplus 正则表达式  分组替换
			editplus :zz正则表达式替换 /开头的api+换行符 替换为 /开头的api+空格 \1后有空格 editplus :zz正则表达式替换 行首两个数字+换行符 替换为 行首两个数字+空 ... 
- 将xml文件转为txt文件
			import os import re import sys import glob import xml.etree.ElementTree as ET def xml_to_txt(indir,o ... 
- 18-vim-插入命令
			在vi中除了常用的i进入编辑模式外,还提供了以下命令同样可以进入编辑模式: 命令 英文 功能 使用频率 i insert 在当前字符前插入文本 常用 I insert 在行首插入文本 较常用 a ap ... 
