Codechef TRIPS Children Trips (分块、倍增)
題目鏈接: https://www.codechef.com/problems/TRIPS
感覺CC有點毒瘤啊。。
題解: 首先有一個性質(zhì)可能是因為太傻所以網(wǎng)上沒人解釋,然而我看了半天: 就是正序和倒序經(jīng)過同一段路徑,用時一樣。(如果您覺得這個性質(zhì)很顯然請不要看我的證明。)
證明: 首先為了讓它看起來好懂一些,考慮以下序列: 1 1 1 2 1 1 1 2 1 1 1 2 1 1 1 2 1 1 1 2, 體力\(p=6\)
則正序在\(1,2,3,4,5\)天后走的路程分別是\(5,10,15,19,20\), 每天所處的最左位置分別為\(1,6,11,16,20\),記該序列為\(sa\)
倒序在\(1,2,3,4,5\)天后走的路程分別為\(4,8,12,16,20\),每天所處的最左位置分別為\(17,13,9,5,1\), 由于是倒序所以翻轉(zhuǎn)這個序列變成\(1,5,9,13,17\),記該序列為\(sb\)
那么我們可以視為每一天正序的開始位置比倒序翻轉(zhuǎn)后的當天開始位置“領(lǐng)先”一段路程,把第\(i\)天領(lǐng)先的路程記作\(t(i)=sa[i]-sb[i]\)
顯然有\(t(i)-t(i-1)\)為\(0\)或\(1\), 為\(1\)當且僅當正序這一天沒有余下體力而倒序這一天余下了體力。
一旦存在一個\(k\)滿足\(sa[k]=sb[k+1]\), 就意味著第\(k\)天正序比倒序領(lǐng)先了一天的路程,那么正序倒序答案就不一樣了。
假設存在,找到最小的\(k\), 則\(sa[k-1]=sb[k]-1\), 也就是在第\((k-1)\)天正序就已經(jīng)“差一步”就領(lǐng)先逆序一天(即到達逆序第\(k\)天的最左位置)了,從而有\(t(k)-t(k-1)=1\). 那么逆序在第\(k\)天一定被一個\(2\)阻擋而余下了\(1\)的體力,而這個\(2\)一定就是下一天,也就是第\((k-1)\)天走的第一條邊(最右位置)。又因為在第\((k-1)\)天“正序最左位置差一步就要到達逆序第\(k\)天的最左位置”,所以第\((k-1)\)天正序最左位置就等于逆序第\((k-1)\)天的最右位置。所以正序第\((k-1)\)天的最左位置的權(quán)值是\(2\). 設此位置為\(pos\).
而逆序第\(k\)天的最左位置是這個位置的下一個,即\(pos+1\). 考慮第\((k-1)\)天,正序要在這一天的最右位置趕上逆序的第\(k\)天的最右位置,那么正序必須比逆序多走過位置\(pos\), 而\(pos\)的權(quán)值是\(2\), 就意味著正序比逆序要多走\(2\)的路程,而這顯然是不能達到的。矛盾,故不存在一個這樣的\(k\).
這玩意真難解釋,我的確想了半天……
發(fā)現(xiàn)了這個性質(zhì)以及其一些簡單的推論,后面的就比較簡單了
分塊討論
對于\(p>\sqrt n\)的詢問,暴力倍增跳即可,最多跳\(\sqrt n\)次。
對于\(p\le \sqrt n\)的詢問,按\(p\)從小到大排序,只有\(O(\sqrt n)\)個不同的\(p\), 對于每一個預處理數(shù)組\(f[i][j]\)表示\(i\)點往上跳\(2^j\)天(不是步)跳到哪里,詢問暴力跳即可
時間復雜度\(O(n\sqrt n\log n)\)
然后盡管復雜度這么大,在CC上測出來時間是\(3.42s\) (時限\(8s\)).
代碼
#include<cstdio> #include<cstdlib> #include<cstring> #include<cassert> #include<algorithm> #include<cmath> using namespace std;const int N = 1e5; const int MXB = 317; const int LGN = 16; struct Edge {int v,w,nxt; } e[(N<<1)+3]; struct Query {int u,v,p,id;bool operator <(const Query &arg) const{return p<arg.p;} } qr[N+3]; int fe[N+3]; int fa[N+3][LGN+3]; int dep[N+3],dis[N+3]; int f[N+3][LGN+3]; int ans[N+3]; int n,q,en,B;void addedge(int u,int v,int w) {en++; e[en].v = v; e[en].w = w;e[en].nxt = fe[u]; fe[u] = en; }void dfs(int u) {\for(int i=1; i<=LGN; i++) fa[u][i] = fa[fa[u][i-1]][i-1];for(int i=fe[u]; i; i=e[i].nxt){int v = e[i].v;if(v==fa[u][0]) continue;fa[v][0] = u;dep[v] = dep[u]+1; dis[v] = dis[u]+e[i].w;dfs(v);} }int LCA(int u,int v) {if(dep[u]<dep[v]) {swap(u,v);}int dif = dep[u]-dep[v];for(int i=0; i<=LGN; i++) {if(dif&(1<<i)) u = fa[u][i];}if(u==v) return u;for(int i=LGN; i>=0; i--){if(fa[u][i]!=fa[v][i]) {u = fa[u][i],v = fa[v][i];}}return fa[u][0]; }int jump0(int u,int p) {int tdis = dis[u];for(int i=LGN; i>=0; i--){if(fa[u][i]!=0 && tdis-dis[fa[u][i]]<=p) {u = fa[u][i];}}return u; }int jump_large(int &u,int lca,int p) {int ret = 0;while(dis[u]-dis[lca]>=p){u = jump0(u,p);ret++;}return ret; }int solve_large(int u,int v,int p) {int ret = 0; int lca = LCA(u,v);int ret1 = jump_large(u,lca,p),ret2 = jump_large(v,lca,p);ret = ret1+ret2; // printf("u=%d v=%d\n",u,v);if(u==lca && v==lca);else if(dis[u]+dis[v]-2*dis[lca]<=p) ret++;else ret+=2;return ret; }int jump_small(int &u,int lca,int p) {int ret = 0;for(int i=LGN; i>=0; i--){if(dis[f[u][i]]>dis[lca]) {u = f[u][i]; ret += (1<<i);} //> not >= }return ret; }int solve_small(int u,int v,int p) {int ret = 0; int lca = LCA(u,v);int ret1 = jump_small(u,lca,p),ret2 = jump_small(v,lca,p);ret = ret1+ret2; // printf("u=%d v=%d lca%d ret1=%d ret2=%d\n",u,v,lca,ret1,ret2);if(u==lca && v==lca);else if(dis[u]+dis[v]-2*dis[lca]<=p) ret++;else ret+=2;return ret; }int main() {scanf("%d",&n); B = sqrt(n);for(int i=1; i<n; i++){int x,y,z; scanf("%d%d%d",&x,&y,&z);addedge(x,y,z); addedge(y,x,z);}dep[1] = dis[1] = 1; dfs(1);scanf("%d",&q);for(int i=1; i<=q; i++){int u,v,p; scanf("%d%d%d",&qr[i].u,&qr[i].v,&qr[i].p); qr[i].id = i;}sort(qr+1,qr+q+1);for(int i=1; i<=n; i++) f[i][0] = fa[i][0];int id = 0;for(int i=2; i<=B; i++){for(int j=2; j<=n; j++){if(dis[j]-dis[fa[f[j][0]][0]]<=i) {f[j][0] = fa[f[j][0]][0];}}for(int j=1; j<=LGN; j++){for(int k=1; k<=n; k++) f[k][j] = f[f[k][j-1]][j-1];}while(id<q && qr[id+1].p==i){id++;ans[qr[id].id] = solve_small(qr[id].u,qr[id].v,qr[id].p);}}for(id=id+1; id<=q; id++){ans[qr[id].id] = solve_large(qr[id].u,qr[id].v,qr[id].p);}for(int i=1; i<=q; i++) printf("%d\n",ans[i]);return 0; }總結(jié)
以上是生活随笔為你收集整理的Codechef TRIPS Children Trips (分块、倍增)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BZOJ 1859 Luogu P258
- 下一篇: Codechef SEAARC Sere