Codechef TRIPS Children Trips (分块、倍增)
题目链接: https://www.codechef.com/problems/TRIPS
感觉CC有点毒瘤啊。。
题解: 首先有一个性质可能是因为太傻所以网上没人解释,然而我看了半天: 就是正序和倒序经过同一段路径,用时一样。
我原来想了个很麻烦的证法,ckw: "显然把一个序列划分成数量尽可能少的子串,每一段和不超过\(P\), 那么从左往右和从右往左都是最优解,所以他俩相等啊"
发现了这个性质以及其一些简单的推论,后面的就比较简单了
分块讨论
对于\(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;
}
Codechef TRIPS Children Trips (分块、倍增)的更多相关文章
- 【CODECHEF】Children Trips 倍增
此题绝了,$O(n^{1.5}\ log\ n)$都可以过掉.... 题目大意:给你一颗$n$个点的树,每条边边权不是2就是$1$,有$m$个询问,每次询问一个人从$x$点走到$y$点,每天可以走的里 ...
- 【codechef】Children Trips
Portal -->CC_Children Trips Solution (英文题解看得真爽qwq不过写的好详细啊ovo) 首先这题有一个很重要的条件就是边权是\(1\)或者\(2\),所以虽然 ...
- [CC-TRIPS]Children Trips
[CC-TRIPS]Children Trips 题目大意: \(n(n\le10^5)\)座城市构成一棵树,且树上的每条边的长度\(l_i\)满足\(1\le l_i\le 2\).\(m(m\le ...
- CODECHEF Oct. Challenge 2014 Children Trips
@(XSY)[分塊, 倍增] Description There's a new trend among Bytelandian schools. The "Byteland Tourist ...
- 题解 Children Trips
题目传送门 Description 给出一个大小为 \(n\) 的边权全为 \(1,2\) 的带权树,有 \(q\) 此查询,每次给出 \(u,v,p\) ,问 \(u\to v\) 每次可以最多走边 ...
- HDU - 6394 Tree(树分块+倍增)
http://acm.hdu.edu.cn/showproblem.php?pid=6394 题意 给出一棵树,然后每个节点有一个权值,代表这个点可以往上面跳多远,问最少需要多少次可以跳出这颗树 分析 ...
- hdu 6394 Tree (2018 Multi-University Training Contest 7 1009) (树分块+倍增)
链接: http://acm.hdu.edu.cn/showproblem.php?pid=6394 思路:用dfs序处理下树,在用分块,我们只需要维护当前这个点要跳出这个块需要的步数和他跳出这个块去 ...
- CodeChef Chef and Churu [分块]
题意: 单点修改$a$ 询问$a$的区间和$f$的区间和 原来普通计算机是这道题改编的吧... 对$f$分块,预处理$c[i][j]$为块i中$a_j$出现几次,$O(NH(N))$,只要每个块差分加 ...
- (转载)构建public APIs与CORS
from: https://segmentfault.com/a/1190000000709909 理由:在操作层面详细的讲解了跨域的操作.尤其是对于option请求的详解.收藏. 在构建Public ...
随机推荐
- c语言程序命名规范:函数、变量、数组、文件名
函数: //send or recv data task void send_recv_data(void *pvParameters); //get socket error code. retur ...
- 小记---------maxwell启动闪退问题
日志报错信息如下:大致是说因为maxwell在对接mysql时伪装成一个从库slave,但是uuid重复.猜想是其他部门同事也在同时使用maxwell,都使用的是maxwell默认的uuid ,从而导 ...
- Scrapy学习(二)
1.在合适的位置创建一个文件夹,创建python虚拟环境: 2.在虚拟环境中安装Scrapy库和pypiwin32库: 命令:pip install scrapy pip install pypiwi ...
- 抖音很火的存钱计划,让python告诉你总共可以存到多少钱!
抖音上有个很火的存钱计划,说是第一天存1块钱,第二天存2块钱,第三天存3块钱.....依此类推存365天,总共可以存到多少钱,我们现在用python告诉你怎么做: #定个初始存入金额 money = ...
- Electric Charges CodeForces - 623C (二分答案)
大意: 平面上n个点每个点坐标为(x,0)或(0,y), 求任意两点距离平方最大值的最小值. 二分答案, 转化为判定最大值是否<=e, 按$x$排序后, 因为固定左端点, $y$绝对值的最大值是 ...
- 【iOS】正則表達式抓取网页数据制作小词典
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/xn4545945/article/details/37684127 应用程序不一定要自己去提供数据. ...
- asp.net运行原理及机制
当一个HTTP请求到服务器并被IIS接收到之后,IIS首先通过客户端请求的页面类型为其加载相应的.dll文件,然后在处理过程中将这条请求发送给能够处理这个请求的模块.在ASP.NET 3.5中,这个模 ...
- Java学习路线(完整详细版)
Java学习路线(完整详细版) https://jingyan.baidu.com/article/c1a3101e110864de656deb83.html
- WebService简单使用教程
根据说明书获取信息 代码示例: import com.gyf.weather.ws.ArrayOfString; import com.gyf.weather.ws.WeatherWS; import ...
- 安卓的几种alert对话框
@Override public void onClick(View v) { switch (v.getId()) { case R.id.d1: AlertDialog.Builder build ...