1. 程式人生 > >BZOJ 3159 決戰 (splay+樹鏈剖分)

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. Touriststarjan+

傳送門 先把邊雙連通分量用圓方樹一樣的方法縮點,然後把新建的樹樹剖維護。 注意對於邊雙連通分量需要維護動態最小值,可以用 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到節點