題目大意:給一棵樹,每個點一開始顏色互不相同,支援三個操作
1.將乙個點到根的路徑染成一種新的顏色
2.將乙個新的點設為根,並將原來的根到這個點的路徑染成一種新的顏色
3.查詢乙個子樹(對於當前根)到根的路徑期望顏色數
真tm是道神題,idea實在是太妙了
首先由於第2個操作的特殊性,我們可以發現,每種顏色在樹上都是連續的,不會斷開
於是第三個操作就變成了查詢期望顏色段數
然後我們想象,如果乙個點和他父親結點的顏色不同,那就把他到他父親的這條邊權視為1,否則視為0
這樣就變成了查詢期望到根的路徑和+1
然後我們看第乙個操作對於這棵樹上邊權的影響:
這個點到根的路徑全部變成0,與這條路徑相鄰的其他邊都變成1
哇!太tm神奇了!
這是不是很像lct的access操作!
我們把邊權為0的看做實邊,邊權為1的看做虛邊,就和lct一模一樣
那麼假設我們維護乙個lct,這樣就可以在logn的時間內知道要修改哪些邊權了!要修改的邊的數量也是logn級別的!
於是我們可以在最開始樹鏈剖分一下,用一顆線段樹維護每個點到當前根的顏色段數
查詢的時候,只需要用子樹的和除以子樹大小+1就好辣!
等等!他還會換根?!那這子樹怎麼維護?!
其實也可以維護啦!如果做過「bzoj3083 遙遠的國度」就知道啦,只需要分類討論一下就好了,當前的子樹最多在原來的序列中被切成兩段,不影響時間複雜度的!
具體可以看**:
#include#include#include#include#define n 200010
using namespace std;
unsigned int to[n],nxt[n],pre[n],cnt;
unsigned int root=1;
void ae(unsigned int ff,unsigned int tt)
unsigned int siz[n],d[n],zs[n],fa[n];
unsigned int fa[n];
void build1(unsigned int x)
} zs[x]=maxb;
}unsigned int top[n],sit[n],fan[n],cn;
void make(unsigned int x,unsigned int tt)
}unsigned int get(unsigned int x,unsigned int y)
return fan[sit[x]+1];
}unsigned int ch[n][2],l[n],r[n];
unsigned int n,m;
bool rev[n];
void pup(unsigned int x)
void pudrev(unsigned int x)
void pud(unsigned int x)
}bool isroot(unsigned int x)
void rotate(unsigned int x)
unsigned int l=0,r;
if(ch[y][1]==x) l=1;r=l^1;
fa[ch[x][r]]=y;
fa[y]=x;
fa[x]=z;
ch[y][l]=ch[x][r];
ch[x][r]=y;
pup(y);//pup(x);
}unsigned int q[n],tt;
unsigned int l[n<<2],r[n<<2],sum[n<<2],t[n<<2];
void pup(unsigned int x)
void pud(unsigned int x)
void build(unsigned int now,unsigned int ll,unsigned int rr)
unsigned int mid=(ll+rr)>>1;
build(now<<1,ll,mid);
build(now<<1|1,mid+1,rr);
pup(now);
}void change(unsigned int now,unsigned int ll,unsigned int rr,unsigned int v)
pud(now);
unsigned int mid=(l[now]+r[now])>>1;
if(rr<=mid) change(now<<1,ll,rr,v);
else if(ll>mid) change(now<<1|1,ll,rr,v);
else change(now<<1,ll,mid,v),change(now<<1|1,mid+1,rr,v);
pup(now);
}unsigned int check(unsigned int now,unsigned int ll,unsigned int rr)
void splay(unsigned int x)
rotate(x); }}
void changeit(unsigned int x,unsigned int v)
{ if(x==root) change(1,1,n,v);
else if(sit[root]>=sit[x]&&sit[x]+siz[x]>=sit[root]+siz[root])
{ x=get(x,root);
if(sit[x]>1)
change(1,1,sit[x]-1,v);
if(sit[x]+siz[x]-1=sit[x]&&sit[x]+siz[x]>=sit[root]+siz[root])
{ double tmp=0;
x=get(x,root);
if(sit[x]>1)
tmp+=check(1,1,sit[x]-1);
if(sit[x]+siz[x]-1
BZOJ3779 重組病毒
窮哭了 難嗎?難碼.首先觀察一下操作一,就是乙個access,但是要改變子樹啊,lct不緇瓷,所以線段樹稍微維護一下。怎麼維護是乙個大難點啊。是要分類討論的。先找出實右子樹在原數上的根xxx。情況rt x rt x rt x 直接修改整顆樹。r trt rt在子樹中,令y r ty rt y rt ...
bzoj 3779 重組病毒
一道好題 乙個點到根傳染需要的時間是這段路徑上不同顏色的數目,乙個點子樹到根平均傳染時間就是加權平均數了 好像是廢話 所以只要用線段樹維護dfs序就這個可以了,換根的話乙個點的子樹要麼在dfs序中不變,要麼被截成了 1,l 和 r,n 兩段 當這個點為當前root的祖先 l和r即為包含當前根的這個點...
bzoj 3779 重組病毒
黑客們通過對已有的病毒反編譯,將許多不同的病毒重組,並重新編譯出了新型的重組病毒。這種病毒的繁殖和變異能力極強。為了阻止這種病毒傳播,某安全機構策劃了一次實驗,來研究這種病毒。實驗在乙個封閉的區域網內進行。區域網內有n臺計算機,編號為1 n。一些計算機之間通過網線直接相連,形成樹形的結構。區域網中有...