想了巨大久的正解,最後還是只能胡個昨天剛學的點分治暴力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的基本語法知識。全新的介面設計,將會帶來...