BZOJ 3159 決戰 (splay+樹鏈剖分)
題意:給出一棵樹,(1)路徑加一個值;(2)路徑上的節點的值反轉(只是值反轉,不是節點反轉);(3)詢問路徑最大值 最小值 和。
解題思路:
題目簡單,程式碼冗長
寫這道題的時候還沒有學LCT,所以我寫了一種非常奇葩的方法,純splay+樹鏈剖分,程式碼冗長,但我覺得還挺好理解的。
首先我們可以先對整棵樹進行樹鏈剖分,分出輕重鏈之後就可以將一條鏈分解成若干條重鏈,用splay維護剖分出來的序列,查詢最大最小和sum都是splay的基礎操作就不說了,最關鍵的是進行樹鏈翻轉。這裡的確讓我為難了許久,後來我奇葩的想出了一個辦法::將鏈按順序提取出來合併然後進行反轉再拆開按順序放回去,這樣就可以做到下一次查詢這裡正好是反過來的了,然後就是怎麼做的問題了,這裡的確變態,為了不改變點在序列的位置方便提取,我先按照這些條鏈在序列中從後往前提取,最後還要從前往後放回去,不過這裡處理完之後就問題不大了,看著程式碼都是淚啊!!!
(程式碼核能,想看的話請做好心理準備)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
struct splay
{
splay *ls,*rs,*fa;
long long sum,maxx,minn,v,size;
long long lazy_mark;
bool rev_mark;
splay(long long x);
void push_up();
void push_down();
void add(long long x);
void rev();
}*null=new splay(2000209123),*root,*midroot,*swz[100005];
long long t;
long long pou[100000];
long long fir[100005];
long long nex[100005];
long long wz[100000];
struct point
{
long long top,son,fa,size,deep;
}a[100005];
struct bian
{
long long l,r;
}b[100005];
void dfs1(int u,int fro)
{
a[u].size=1;
a[u].deep=a[fro].deep+1;
for(int o=fir[u];o!=0;o=nex[o])
{
if(b[o].r!=fro)
{
a[b[o].r].fa=u;
dfs1(b[o].r,u);
a[u].size+=a[b[o].r].size;
if(a[b[o].r].size>a[a[u].son].size) a[u].son=b[o].r;
}
}
}
void dfs2(int u,int fro)
{
pou[++t]=u;
wz[u]=t;
if(u==a[a[u].fa].son) a[u].top=a[a[u].fa].top;
else a[u].top=u;
if(a[u].son) dfs2(a[u].son,u);
for(int o=fir[u];o!=0;o=nex[o])
{
if(b[o].r!=fro && b[o].r!=a[u].son) dfs2(b[o].r,u);
}
}
splay :: splay(long long x)
{
ls=rs=fa=null;
sum=maxx=minn=v=x;
lazy_mark=rev_mark=0;
size=null ? 1 : 0;
}
void splay :: add(long long x)
{
if(this==null) return;
lazy_mark+=x;
push_down();
}
void splay :: rev()
{
if(this==null) return;
rev_mark^=1;
push_down();
}
void splay :: push_up()
{
if(ls!=null) ls->push_down();
if(rs!=null) rs->push_down();
size=ls->size+rs->size+1;
maxx=0;
if(ls->maxx>maxx && ls!=null && ls->v!=2000209123) maxx=ls->maxx;
if(rs->maxx>maxx && rs!=null && rs->v!=2000209123) maxx=rs->maxx;
if(v>maxx) maxx=v;
minn=2147483647;
if(ls->minn<minn && ls!=null && ls->v!=2000209123) minn=ls->minn;
if(rs->minn<minn && rs!=null && rs->v!=2000209123) minn=rs->minn;
if(v<minn) minn=v;
sum=v;
if(ls->v!=2000209123 && ls!=null) sum+=ls->sum;
if(rs->v!=2000209123 && rs!=null) sum+=rs->sum;
}
void splay :: push_down()
{
if(rev_mark)
{
swap(ls,rs);
rev_mark=0;
if(ls!=null && ls->v!=2000209123) ls->rev_mark^=1;
if(rs!=null && rs->v!=2000209123) rs->rev_mark^=1;
}
if(lazy_mark)
{
v+=lazy_mark;
minn+=lazy_mark;
maxx+=lazy_mark;
sum+=lazy_mark*size;
if(ls!=null && ls->v!=2000209123) ls->lazy_mark+=lazy_mark;
if(rs!=null && rs->v!=2000209123) rs->lazy_mark+=lazy_mark;
lazy_mark=0;
}
}
void left(splay *x)
{
splay *y=x->fa;
y->rs=x->ls;
x->ls->fa=y;
x->ls=y;
x->fa=y->fa;
if(y==y->fa->ls) y->fa->ls=x;
else if(y==y->fa->rs) y->fa->rs=x;
y->fa=x;
y->push_up();
if(y==root) root=x;
}
void right(splay *x)
{
splay *y=x->fa;
y->ls=x->rs;
x->rs->fa=y;
x->rs=y;
x->fa=y->fa;
if(y==y->fa->ls) y->fa->ls=x;
else if(y==y->fa->rs) y->fa->rs=x;
y->fa=x;
y->push_up();
if(y==root) root=x;
}
void splaying(splay *x,splay *goal)
{
while(1)
{
splay *y=x->fa;
splay *z=y->fa;
if(y==goal) break;
if(z==goal)
{
if(x==y->ls) right(x);
else left(x);
break;
}
if(x==y->ls)
{
if(y==z->ls) right(y);
right(x);
}
else
{
if(y==z->rs) left(y);
left(x);
}
}
x->push_up();
}
void my_find(splay *x,long long mc,splay *z)
{
while(1)
{
x->push_down();
if(mc<=x->ls->size) x=x->ls;
else
{
mc-=x->ls->size;
if(mc==1) break;
mc--;
x=x->rs;
}
}
splaying(x,z);
midroot=x;
}
void add()
{
long long x,y,v;
scanf("%lld%lld%lld",&x,&y,&v);
while(a[x].top!=a[y].top)
{
if(a[a[x].top].deep<a[a[y].top].deep) swap(x,y);
my_find(root,wz[a[x].top],null);
my_find(root,wz[x]+2,root);
root->rs->ls->add(v);
x=a[a[x].top].fa;
}
if(a[x].deep<a[y].deep) swap(x,y);
my_find(root,wz[y],null);
my_find(root,wz[x]+2,root);
root->rs->ls->add(v);
root->rs->push_up();
root->push_up();
}
splay* maketree(long long l,long long r)
{
if(l>r) return null;
long long mid=l+r>>1;
splay *sp=new splay(0);
sp->ls=maketree(l,mid-1);
sp->rs=maketree(mid+1,r);
sp->ls->fa=sp->rs->fa=sp;
sp->push_up();
swz[mid]=sp;
return sp;
}
void init()
{
root=new splay(2000209123);
root->rs=new splay(2000209123);
root->rs->fa=root;
root->rs->ls=maketree(1,t);
root->rs->ls->fa=root->rs;
root->rs->push_up();
root->push_up();
}
int find_lca(int x,int y)
{
while(a[x].top!=a[y].top)
{
if(a[a[x].top].deep<a[a[y].top].deep) swap(x,y);
x=a[a[x].top].fa;
}
if(a[x].deep<a[y].deep) swap(x,y);
return y;
}
void my_merge(splay* x,splay* y)
{
my_find(x,x->size,null);
midroot->rs=y;
y->fa=midroot;
midroot->push_up();
}
splay* tiqu(int l,int r,splay* &o)
{
my_find(o,l,null);
my_find(o,r+2,root);
splay *mid=o->rs->ls;
mid->fa=null;
o->rs->ls=null;
o->rs->push_up();
o->push_up();
return mid;
}
struct tiquchulaide
{
int num,shou,len;
splay* ti,*newti;
}midd[50000];
bool cmp1(tiquchulaide a,tiquchulaide b)
{
return a.num<b.num;
}
bool cmp2(tiquchulaide a,tiquchulaide b)
{
return a.shou>b.shou;
}
bool cmp3(tiquchulaide a,tiquchulaide b)
{
return a.shou<b.shou;
}
int shou[50000];
int len[50000];
int top=0;
void y_find(int y,int lca)
{
if(y==lca) return;
if(a[y].top==a[lca].top)
{
shou[++top]=wz[lca]+1;
len[top]=wz[y]-wz[lca];
midd[top].num=top;
midd[top].shou=shou[top];
midd[top].len=len[top];
return;
}
y_find(a[a[y].top].fa,lca);
shou[++top]=wz[a[y].top];
len[top]=wz[y]-wz[a[y].top]+1;
midd[top].num=top;
midd[top].shou=shou[top];
midd[top].len=len[top];
}
void fanzhuanqujian()
{
long long jilu=0;
long long x,y;
scanf("%lld%lld",&x,&y);
top=0;
long long lca=find_lca(x,y);
while(x!=lca)
{
if(a[x].top==a[lca].top)
{
shou[++top]=wz[lca];
len[top]=wz[x]-wz[lca]+1;
midd[top].num=top;
midd[top].shou=shou[top];
midd[top].len=len[top];
break;
}
shou[++top]=wz[a[x].top];
len[top]=wz[x]-wz[a[x].top]+1;
midd[top].num=top;
midd[top].shou=shou[top];
midd[top].len=len[top];
x=a[a[x].top].fa;
}
jilu=top;
if(x==lca)
{
shou[++top]=wz[lca];
midd[top].shou=wz[lca];
midd[top].len=1;
midd[top].num=top;
len[top]=1;
}
y_find(y,lca);
sort(midd+1,midd+1+top,cmp2);
for(int i=1;i<=top;i++)
{
midd[i].ti=tiqu(midd[i].shou,midd[i].shou+midd[i].len-1,root);
if(midd[i].num<=jilu) midd[i].ti->rev();
}
sort(midd+1,midd+1+top,cmp1);
midroot=null;
for(int i=1;i<=top;i++)
{
//cout<<midroot->size<<" ";
if(midroot==null) midroot=midd[i].ti;
else my_merge(midroot,midd[i].ti);
}
//cout<<midroot->size<<endl;
midroot->rev();
for(int i=1;i<=top;i++)
{
//cout<<midroot->size<<endl;
my_find(midroot,midd[i].len,null);
//cout<<midd[i].shou<<" "<<midd[i].len<<endl;
splay *middd=midroot->rs;
middd->fa=null;
midroot->rs=null;
midroot->push_up();
midd[i].newti=midroot;
if(midd[i].num<=jilu) midd[i].newti->rev();
midroot=middd;
}
sort(midd+1,midd+1+top,cmp3);
for(int i=1;i<=top;i++)
{
my_find(root,midd[i].shou,null);
my_find(root,midd[i].shou+1,root);
root->rs->ls=midd[i].newti;
root->rs->ls->fa=root->rs;
root->rs->push_up();
root->push_up();
}
}
void sum()
{
long long x,y;
scanf("%lld%lld",&x,&y);
long long ans=0;
while(a[x].top!=a[y].top)
{
if(a[a[x].top].deep<a[a[y].top].deep) swap(x,y);
my_find(root,wz[a[x].top],null);
my_find(root,wz[x]+2,root);
ans+=root->rs->ls->sum;
x=a[a[x].top].fa;
}
if(a[x].deep<a[y].deep) swap(x,y);
my_find(root,wz[y],null);
my_find(root,wz[x]+2,root);
ans+=root->rs->ls->sum;
printf("%lld\n",ans);
}
void major()
{
long long maxx=-214748364700000000ll;
long long x,y;
scanf("%d%d",&x,&y);
long long ans=0;
while(a[x].top!=a[y].top)
{
if(a[a[x].top].deep<a[a[y].top].deep) swap(x,y);
my_find(root,wz[a[x].top],null);
my_find(root,wz[x]+2,root);
if(root->rs->ls->maxx>maxx) maxx=root->rs->ls->maxx;
x=a[a[x].top].fa;
}
if(a[x].deep<a[y].deep) swap(x,y);
my_find(root,wz[y],null);
my_find(root,wz[x]+2,root);
if(root->rs->ls->maxx>maxx) maxx=root->rs->ls->maxx;
printf("%lld\n",maxx);
}
void minor()
{
long long minn=214748364700000000ll;
long long x,y;
scanf("%lld%lld",&x,&y);
long long ans=0;
while(a[x].top!=a[y].top)
{
if(a[a[x].top].deep<a[a[y].top].deep) swap(x,y);
my_find(root,wz[a[x].top],null);
my_find(root,wz[x]+2,root);
if(root->rs->ls->minn<minn) minn=root->rs->ls->minn;
x=a[a[x].top].fa;
}
if(a[x].deep<a[y].deep) swap(x,y);
my_find(root,wz[y],null);
my_find(root,wz[x]+2,root);
if(root->rs->ls->minn<minn) minn=root->rs->ls->minn;
printf("%lld\n",minn);
}
char s[10];
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
long long n,m,r;
scanf("%lld%lld%lld",&n,&m,&r);
for(int i=1;i<n;i++)
{
scanf("%d%d",&b[i].l,&b[i].r);
b[n+i].l=b[i].r;
b[n+i].r=b[i].l;
nex[i]=fir[b[i].l];
fir[b[i].l]=i;
nex[n+i]=fir[b[i].r];
fir[b[i].r]=n+i;
}
int t=rand()%n+1;
dfs1(t,0);
dfs2(t,0);
init();
//for(int i=1;i<=t;i++) cout<<wz[i]<<" ";
for(int k=1;k<=m;k++)
{
scanf("%s",s);
if(s[2]=='c') add();
if(s[2]=='m') sum();
if(s[2]=='j') major();
if(s[2]=='n') minor();
if(s[2]=='v') fanzhuanqujian();
}
return 0;
}
相關推薦
BZOJ 3159 決戰 (splay+樹鏈剖分)
題意:給出一棵樹,(1)路徑加一個值;(2)路徑上的節點的值反轉(只是值反轉,不是節點反轉);(3)詢問路徑最大值 最小值 和。 解題思路: 題目簡單,程式碼冗長 寫這道題的時候還沒有學LCT,所以我寫了一種非常奇葩的方法,純splay+樹鏈剖分,程式碼冗
BZOJ-3626:LCA(離線+樹鏈剖分)
dep bzoj inpu 轉化 輸出 深度 定義 區間 三元組 Description 給出一個n個節點的有根樹(編號為0到n-1,根節點為0)。一個點的深度定義為這個節點到根的距離+1。設dep[i]表示點i的深度,LCA(i,j)表示i與j的最近公共祖先。有q次
2018.11.09 codeforces487E. Tourists(tarjan+樹鏈剖分)
傳送門 先把邊雙連通分量用圓方樹一樣的方法縮點,然後把新建的樹樹剖維護。 注意對於邊雙連通分量需要維護動態最小值,可以用 m u
SPOJ 375 Query on a tree(初學樹鏈剖分)
You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions
bzoj 3631 松鼠的新家 (樹鏈剖分)
連結: https://www.lydsy.com/JudgeOnline/problem.php?id=3631 思路: 直接用樹鏈剖分求每一次運動,因為這道題只需要區間增添,單點求值,沒必要用線段樹,直接陣列標記下就好了 實現程式碼 #include<bits
HDU 5044 Tree(樹鏈剖分)
int str ans hang line sin _id rgb php HDU 5044 Tree field=problem&key=2014+ACM%2FICPC+Asia+Regional+Shanghai
bzoj 4034: [HAOI2015]樹上操作——樹鏈剖分
技術 ins 最大的 輸出 urn 第一個 nbsp void 三種 Description 有一棵點數為 N 的樹,以點 1 為根,且樹點有邊權。然後有 M 個 操作,分為三種: 操作 1 :把某個節點 x 的點權增加 a 。 操作 2 :把某個節點 x 為根的子樹中所
刷題總結——騎士的旅行(bzoj4336 樹鏈剖分套權值線段樹)
次數 || 會有 bzoj 表示 可能 clas calc() 輸入 題目: Description 在一片古老的土地上,有一個繁榮的文明。 這片大地幾乎被森林覆蓋,有N座城坐落其中。巧合的是,這N座城由恰好N-1條雙 向道路連接起來,使得任意兩座城都是連通的。也就是說,
浴谷金秋線上集訓營 T11738 偽神(樹鏈剖分)
ostream algo lap sed hide lose pla std date 先樹鏈剖分,一棵子樹的編號在數組上連續,一條鏈用樹鏈剖分,把這些線段全部取出來,做差分,找到有多少點被>=t條線段覆蓋即可。 #include<iostream
BZOJ 4034: [HAOI2015]樹上操作 樹鏈剖分 線段樹
|| 線段 top www. img 復習 如果 ext hide http://www.lydsy.com/JudgeOnline/problem.php?id=4034 算是對線段樹的一個復習,樹鏈剖分+區間增減單點增減區間查詢。真的簡單到不用lca,但是線段樹又寫
【Luogu3398】倉鼠找sugar(樹鏈剖分)
name -m pac tor int modify 可能 iostream algorithm 【Luogu3398】倉鼠找sugar(樹鏈剖分) 題面 題目描述 小倉鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每個節點的編號為1~n。地下洞穴是一個樹形結構
洛谷P3379 【模板】最近公共祖先(LCA)(樹鏈剖分)
樹鏈剖分 turn 規模 一次 .org pen 整數 src namespace 題目描述 如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。 輸入輸出格式 輸入格式: 第一行包含三個正整數N、M、S,分別表示樹的結點個數、詢問的個數和樹根結點的
1036: [ZJOI2008]樹的統計Count(樹鏈剖分)
sca pan blog class str center scu color name 1036: [ZJOI2008]樹的統計Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 19830 Solved:
bzoj3626: [LNOI2014]LCA (樹鏈剖分)
clas con blog top return print post 節點 std 很神奇的方法 感覺是有生之年都想不到正解的這種 考慮對i 到根的節點權值 + 1,則從根到z的路徑和就是lca(i,z)的深度 所以依次把0 ~ n - 1的點權值 + 1 對於
bzoj 4034[HAOI2015]樹上操作 - 樹鏈剖分
絕對值 int lld += splay sed ring print esp 4034: [HAOI2015]樹上操作 Time Limit: 10 Sec Memory Limit: 256 MB Description 有一棵點數為 N 的樹,以點 1 為根,且
BZOJ4196: [Noi2015]軟件包管理器(樹鏈剖分)
linu scanf 空格 思路 通過 其中 如果 scrip read Description Linux用戶和OSX用戶一定對軟件包管理器不會陌生。通過軟件包管理器,你可以通過一行命令安裝某一個軟件包,然後軟件包管理器會幫助你從軟件源下載軟件包,同時自動解決所有
BZOJ4448 SCOI2015情報傳遞(離線+樹鏈剖分+樹狀數組)
fin tree amp tdi 開始 情報 路徑 stream from 即滋磁單點修改,詢問路徑上小於某數的值有多少個。暴力樹剖套個主席樹即可,然而不太優美。 開始覺得可以cdq,然而就變成log^3了。冷靜一下感覺簡直是個弱智,修改本身就是單調的,只要對詢問離
2018.10.27 bzoj1984: 月下“毛景樹”(樹鏈剖分)
傳送門 唉蒟蒻又退化了,這道sb題居然做了20min,最後發現是 u p d
bzoj4034: [HAOI2015]樹上操作(樹鏈剖分)
bzoj4034 題目描述:給定一個n個點的樹,有m次操作,操作分3種。 1、將某個節點的點權增加x。 &nb
bzoj2243: [SDOI2011]染色(樹鏈剖分)
bzoj2243 樹鏈剖分好題啊! 題目描述:給定一顆n個點的樹,有m個操作,操作有兩種。 1、將節點a到節點