Codechef TRIPS Children Trips (分块、倍增)
作者:互联网
题目链接: https://www.codechef.com/problems/TRIPS
感觉CC有点毒瘤啊。。
题解: 首先有一个性质可能是因为太傻所以网上没人解释,然而我看了半天: 就是正序和倒序经过同一段路径,用时一样。(如果您觉得这个性质很显然请不要看我的证明。)
证明: 首先为了让它看起来好懂一些,考虑以下序列: 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\), 由于是倒序所以翻转这个序列变成\(1,5,9,13,17\),记该序列为\(sb\)
那么我们可以视为每一天正序的开始位置比倒序翻转后的当天开始位置“领先”一段路程,把第\(i\)天领先的路程记作\(t(i)=sa[i]-sb[i]\)
显然有\(t(i)-t(i-1)\)为\(0\)或\(1\), 为\(1\)当且仅当正序这一天没有余下体力而倒序这一天余下了体力。
一旦存在一个\(k\)满足\(sa[k]=sb[k+1]\), 就意味着第\(k\)天正序比倒序领先了一天的路程,那么正序倒序答案就不一样了。
假设存在,找到最小的\(k\), 则\(sa[k-1]=sb[k]-1\), 也就是在第\((k-1)\)天正序就已经“差一步”就领先逆序一天(即到达逆序第\(k\)天的最左位置)了,从而有\(t(k)-t(k-1)=1\). 那么逆序在第\(k\)天一定被一个\(2\)阻挡而余下了\(1\)的体力,而这个\(2\)一定就是下一天,也就是第\((k-1)\)天走的第一条边(最右位置)。又因为在第\((k-1)\)天“正序最左位置差一步就要到达逆序第\(k\)天的最左位置”,所以第\((k-1)\)天正序最左位置就等于逆序第\((k-1)\)天的最右位置。所以正序第\((k-1)\)天的最左位置的权值是\(2\). 设此位置为\(pos\).
而逆序第\(k\)天的最左位置是这个位置的下一个,即\(pos+1\). 考虑第\((k-1)\)天,正序要在这一天的最右位置赶上逆序的第\(k\)天的最右位置,那么正序必须比逆序多走过位置\(pos\), 而\(pos\)的权值是\(2\), 就意味着正序比逆序要多走\(2\)的路程,而这显然是不能达到的。矛盾,故不存在一个这样的\(k\).
这玩意真难解释,我的确想了半天……
发现了这个性质以及其一些简单的推论,后面的就比较简单了
分块讨论
对于\(p>\sqrt n\)的询问,暴力倍增跳即可,最多跳\(\sqrt n\)次。
对于\(p\le \sqrt n\)的询问,按\(p\)从小到大排序,只有\(O(\sqrt n)\)个不同的\(p\), 对于每一个预处理数组\(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;
}
标签:倒序,正序,Children,int,Trips,lca,逆序,TRIPS,dis 来源: https://www.cnblogs.com/suncongbo/p/11105116.html