BZOJ3779 重組病毒

2021-07-12 04:17:07 字數 2911 閱讀 3626

題目大意:給一棵樹,每個點一開始顏色互不相同,支援三個操作

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。一些計算機之間通過網線直接相連,形成樹形的結構。區域網中有...