來點好康的

2022-09-14 19:06:10 字數 4005 閱讀 7485

想了巨大久的正解,最後還是只能胡個昨天剛學的點分治暴力qwq。

\(\rm 2s\ 512mb.\)

\(\tt ppl\) 在 \(\rm zbqc\) 隨機遊走累了,現在他想嘗試一些好康的路徑。

由於眾所周知的原因,\(\rm zbqc\) 非常小,小的兩點之間有且僅有一條路徑,也就是說可以視為一棵樹。

\(\tt ppl\) 認為好康的路徑 \(x\rightarrow y\) 應該滿足 \(x\) 是路徑上編號最小的點,\(y\) 是路徑上編號最大的點。

\(\tt ppl\) 決定你告訴他有多少條好康的路徑後就把這些路徑全部走一遍,你能告訴他嗎?

\(1\le n\le 2\times 10^6.\)

現在是亂扯部分,如果要直接看正解請往下滑。

佛了,最近幾天做點分治人麻了,看到這道題就想點分治,最後試圖 \(o(n)\) 解決二維偏序無果,只能胡了個 \(o(n\log_2^2n)\) 的暴力,拿到了 \(60pts.\)

我已經迫不及待想講我的點分治做法了。

首先直接點分治,然後考慮過 \(u\) 的路徑咋算貢獻。考慮處理出一下兩種以 \(u\) 為一端,\(v\) 為另外一端的路徑:

單鏈很好算答案,這裡我們講一講怎麼把兩條鏈拼起來算答案。

假如我們有第一類路徑 \(a\) 和第二類路徑 \(b\),他們能拼起來當且僅當 \(a_v\) 是兩條路徑的最大值,\(b_v\) 是兩條路徑的最小值。

我們對這兩種路徑都記錄一下它們的最大值和最小值,那麼它們應該滿足這個關係:\(b_

但事實上我們只用考慮 \(b_和 \(a_這兩個限制,因為其它限制一定滿足。

然後發現這就是二維偏序,直接排序+樹狀陣列搞定,注意在同一棵子樹內的不能算,所以要減掉。總時間複雜度為 \(o(n\log_2^2n)\)。

掛乙份暴力以證明我沒在胡扯

//12252024832524

#include #define tt templateusing namespace std;

typedef long long ll;

const int maxn = 2000005;

int n;

ll ans;

ll read()

while(c >= '0' && c <= '9')

return x * f;

}tt void put1(t x)

tt void put(t x,char c = -1)

tt t max(t x,t y)

tt t min(t x,t y)

tt t abs(t x)

int head[maxn],tot;

struct edge

e[maxn<<1];

void add_edge(int x,int y)

; head[x] = tot;

}void add_double_edge(int x,int y)

bool vis[maxn];

int max[maxn],siz[maxn],rt;

void getrt(int x,int fa,int s)

max[x] = max(max[x],s-siz[x]);

if(max[x] < max[rt] || !rt) rt = x;

}void predfs(int x,int fa)

}int b[maxn];

int lowbit(int x)

void add(int x,int val)

int sum(int x)

int mat,mit;

struct node

ma[maxn],mi[maxn];

vectorfkma[maxn],fkmi[maxn];

void getm(int x,int fa,int min,int max,int cao)

,fkmi[cao].emplace_back(node);

if(x == max) ma[++mat] = node,fkma[cao].emplace_back(node);

for(int i = head[x],v; i ;i = e[i].nxt)

if(!vis[v = e[i].v] && v != fa)

getm(v,x,min(min,v),max(max,v),cao);

}void solve(int x)

for(int i = head[x],v; i ;i = e[i].nxt)

if(!vis[v = e[i].v])

);sort(fkmi[v].begin(),fkmi[v].end(),(node a,node b));

int now = 0,lenmi = fkmi[v].size();

for(int i = 0,lenma = fkma[v].size();i < lenma;++ i)

for(int i = 0;i < now;++ i) add(fkmi[v][i].x,-1);

fkmi[v].clear(); fkma[v].clear();

} sort(ma,ma+mat+1,(node a,node b));

sort(mi,mi+mit+1,(node a,node b));

int now = 1;

for(int i = 1;i <= mat;++ i)

for(int i = 1;i < now;++ i) add(mi[i].x,-1);

// printf("solve22 %d %lld\n",x,ans);

}void dfs(int x)

}int main()/*6

0 1 2 2 3 5

12*/

接下來是正解。

解法很奇妙,我想不到。

考慮路徑的最值能想到什麼?\(\tt kruskal\) 重構樹!這道題是點權,所以我們可以建特殊的重構樹。

我們需要建兩棵重構樹,保證 \(lca(x,y)\) 是 \(x\rightarrow y\) 路徑上的最大值/最小值,這個過程可以用並查集逆序加邊實現。

然後我們的問題就轉化為有多少個點對在一棵樹上是祖先-後代關係而在另一棵樹上是後代-祖先關係。

其實也是乙個偏序問題,直接樹狀陣列即可。

總時間複雜度 \(o(n\log_2n)\),常數極小。

經典正解比暴力短

//12252024832524

#include #define tt templateusing namespace std;

typedef long long ll;

const int maxn = 2000005;

int n;

ll ans;

ll read()

while(c >= '0' && c <= '9')

return x * f;

}tt void put1(t x)

tt void put(t x,char c = -1)

tt t max(t x,t y)

tt t min(t x,t y)

tt t abs(t x)

int head[3][maxn],tot;

struct edge

e[maxn<<2];

void add_edge(int opt,int x,int y)

; head[opt][x] = tot;

}void add_double_edge(int x,int y)

int f[maxn];

int findset(int x)

int b[maxn];

int lowbit(int x)

void add(int x,int val)

int sum(int x)

int dfn[maxn],dfntot,siz[maxn];

void dfs1(int x)

void dfs2(int x)

int main()

Docker 來點好玩的

經過上篇docker入門後,肯定有點抱怨,還是沒有體驗到docker的威力。現在我們來點好玩的。以前我們裝ubuntu,都是要費老大的力氣,有了docker,我們裝個ubuntu玩玩。docker run t i ubuntu 14.04 bin bash好了,你裝好了ubuntu了,可以在裡面隨便...

來點陽光點的

9x年,在上高中吧,一部日劇的主題曲,故事很感人,很喜歡 大佬 的風趣幽默,最忘記不了是這首 陽光 的主題曲。屋簷下的兄弟姐妹都遇上人生的難事,每當事情明朗時,一放這首曲子,我眼睛又溼了 哎,哈哈。最喜歡這句話 流淚是對眼睛最好的運動,感動是對心靈最好的清潔!有機會一起 再感動一翻吧。911 the...

來點娛樂的節目!

第一次寫這個,也不曉得寫什麼好,就隨便來點了。天地四方為江湖,世人聰明反糊塗。名利場上風浪起,贏到頭來還是輸。你好!這是你第一次使用markdown編輯器所展示的歡迎頁。如果你想學習如何使用markdown編輯器,可以仔細閱讀這篇文章,了解一下markdown的基本語法知識。全新的介面設計,將會帶來...