E Explorer

题意:给出一个无向图,每条边有一个通过人数的上限和下限,一群人要一起从1号点走到n号点,这一群人一起走不能分开,问这群人的人数有多少种可以满足条件。

解法:不会做题解参考https://blog.csdn.net/qq_41955236/article/details/99229810这位大佬的,讲得巨好。简单来说就是把原区间离散化(注意这里要有特殊的离散化技巧,①把原区间右端点+1②原区间就代表离散化后的[l,r-1],③此时离散化后的每个点代表的是该点到该点右边一个点的区间,即该点的贡献是r[i+1]-l[i])。)把离散化后的下标建一棵线段树,然后把原图上的边插入到相应的区间结点上(如[1,3]这条边就插入到[1,2][3,4]这两个结点)。然后就在线段树上分治寻找满足要求的答案区间,这一步具体来说就是从跟开始不断往下分治,把当前点存下的边加入到图中,如果此时的图1到n连通统计答案分治停止,否则继续分治直至连通或到叶子结点。

怎么一边分治一边判断连通呢?这里用到并查集判断连通的技巧,从上往下加边就在并查集把边的两个端点集合合并,然后如果1和n在同个集合就是连通。然后注意回溯的时候还原并查集,同时因为有回溯操作,并查集不能路径压缩而使用启发式合并。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=2e5+;
  4. typedef long long LL;
  5. int n,m,fa[N],sz[N];
  6. LL ans;
  7. struct edge{
  8. int u,v,l,r;
  9. }e[N];
  10.  
  11. int getfa(int x) { return x==fa[x] ? x : getfa(fa[x]); }
  12.  
  13. vector<int> tree[N<<],b;
  14. void update(int rt,int l,int r,int ql,int qr,int id) {
  15. if (ql<=l && r<=qr) {
  16. tree[rt].push_back(id);
  17. return;
  18. }
  19. int mid=(l+r)>>;
  20. if (ql<=mid) update(rt<<,l,mid,ql,qr,id);
  21. if (qr>mid) update(rt<<|,mid+,r,ql,qr,id);
  22. }
  23.  
  24. void dfs(int rt,int l,int r) {
  25. vector<int> rec; rec.clear();
  26. int mid=(l+r)>>;
  27. for (int i=;i<tree[rt].size();i++) {
  28. int x=e[tree[rt][i]].u,y=e[tree[rt][i]].v;
  29. int fx=getfa(x),fy=getfa(y);
  30. if (sz[fx]>sz[fy]) swap(fx,fy);
  31. rec.push_back(fx);
  32. fa[fx]=fa[fy]; sz[fy]+=sz[fx];
  33. }
  34. if (getfa()==getfa(n)) ans+=b[r]-b[l-]; //每个点代表的是该点到右边一个点的区间值
  35. else if (l<r) dfs(rt<<,l,mid),dfs(rt<<|,mid+,r);
  36. for (int i=;i<rec.size();i++) fa[rec[i]]=rec[i];
  37. }
  38.  
  39. int main()
  40. {
  41. cin>>n>>m;
  42. for (int i=;i<=m;i++) {
  43. scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].r);
  44. b.push_back(e[i].l); b.push_back(e[i].r+); //右端点+1
  45. }
  46. sort(b.begin(),b.end());
  47. b.erase(unique(b.begin(),b.end()),b.end());
  48. for (int i=;i<=m;i++) {
  49. int l=lower_bound(b.begin(),b.end(),e[i].l)-b.begin()+;
  50. int r=lower_bound(b.begin(),b.end(),e[i].r+)-b.begin()+;
  51. update(,,b.size(),l,r-,i); //原区间离散化后是[l,r-1]
  52. }
  53.  
  54. for (int i=;i<=n;i++) fa[i]=i,sz[i]=;
  55. dfs(,,b.size());
  56. cout<<ans<<endl;
  57. return ;
  58. }

D Distance

题意:在一个n*m*h (n*m*h<=1e5) 的三维平面上,有两种操作,操作一是在某个点打个标记,操作二是询问距离某个点最近的标记输出距离。

解法:比赛的时候写了过KD-tree果断TLE了。赛后才知道正解是定期重构。

具体来说:先是我们定义一个dis数组就是所有操作点作为起点在图上跑bfs得到的最小距离。那么我们先用一个队列v把打标记操作囤起来,当这个队列的标记操作达到sqrt(n*m*h)的时候,把队列里的所有元素(有sqrt(n*m*h)个)作为起点跑一次bfs去更新dis数组,没有达到sqrt(nmh)个的时候就先不管先囤着。那么怎么处理询问呢?对于一个询问它的答案ans首先等于dis[x][y][z],这是代表已经在图上跑过bfs的点(已经屯好的点)的答案,那么还有还在囤在队列v的点就直接暴力更新,因为此时队列v的点不会超过sqrt(nmh)个也不会太慢。

定期重构思想有点像分块,只不过是把操作分块了而不是像分块那样把数据分块。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1e5+;
  4. const int dx[]={-,,,,,};
  5. const int dy[]={,,-,,,};
  6. const int dz[]={,,,,-,};
  7. struct dat{ int x,y,z; };
  8. int n,m,h,T,dis[N];
  9. vector<dat> v;
  10.  
  11. int id(int x,int y,int z) { return (x-)*m*h+(y-)*h+z; }
  12.  
  13. queue<dat> q;
  14. void bfs() {
  15. while (!q.empty()) q.pop();
  16. for (int i=;i<v.size();i++) {
  17. q.push((dat){v[i].x,v[i].y,v[i].z});
  18. dis[id(v[i].x,v[i].y,v[i].z)]=;
  19. }
  20. while (!q.empty()) {
  21. dat u=q.front(); q.pop();
  22. for (int i=;i<;i++) {
  23. int nx=u.x+dx[i],ny=u.y+dy[i],nz=u.z+dz[i];
  24. if (nx< || nx>n || ny< || ny>m || nz< || nz>h) continue;
  25. if (dis[id(nx,ny,nz)]>dis[id(u.x,u.y,u.z)]+) {
  26. dis[id(nx,ny,nz)]=dis[id(u.x,u.y,u.z)]+;
  27. q.push((dat){nx,ny,nz});
  28. }
  29. }
  30. }
  31. }
  32.  
  33. int main()
  34. {
  35. cin>>n>>m>>h>>T;
  36. int k=(int)sqrt(n*m*h);
  37. memset(dis,0x3f,sizeof(dis));
  38. while (T--) {
  39. int opt,x,y,z; scanf("%d%d%d%d",&opt,&x,&y,&z);
  40. if (opt==) {
  41. v.push_back((dat){x,y,z});
  42. if (v.size()>=k) { //定期重构
  43. bfs();
  44. v.clear();
  45. }
  46. } else {
  47. int ans=dis[id(x,y,z)];
  48. for (int i=;i<v.size();i++)
  49. ans=min(ans,abs(x-v[i].x)+abs(y-v[i].y)+abs(z-v[i].z));
  50. printf("%d\n",ans);
  51. }
  52. }
  53. return ;
  54. }

另外还有一种思维上比较”暴力“的解法:分八种情况建立三维BIT查询最近点对。不难理解这种办法是对的,其实就是分八字情况把绝对值符号去掉了。

但是这种办法实现起来可能会有比较多的技巧,首先就是用怎样的数据结构来存储n*m*h<=1e5的三维BIT?这里用两种办法:第一是常见的通过映射把三维拍成一维数组方式,第二是用三重vector来实现不定长三维数组。然后下一个技巧就是怎么快速讨论八种情况,这里一个比较巧妙地办法是通过0-7地二进制位来决定x/y/z的正负,同时也是用0-7的二进制决定查询x/yz的前半部分还是后半部分。

代价几乎就是抄袭(读书人的事。咳咳)https://www.cnblogs.com/Cwolf9/p/11333344.html这位大佬的。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef vector<int> VI;
  4. typedef vector<VI> VVI;
  5. typedef vector<VVI> VVVI;
  6. const int INF=0x3f3f3f3f;
  7. int n,m,h,T;
  8.  
  9. struct BIT{
  10. int n,m,h;
  11. VVVI bit;
  12. void init(int _n,int _m,int _h) { //初始化好大小就可以直接当作三维数组使用
  13. n=_n; m=_m; h=_h;
  14. bit=VVVI(n+,VVI(m+,VI(h+,INF)));
  15. }
  16. void update(int x,int y,int z,int v) {
  17. for (int i=x;i<=n;i+=i&-i)
  18. for (int j=y;j<=m;j+=j&-j)
  19. for (int k=z;k<=h;k+=k&-k)
  20. bit[i][j][k]=min(bit[i][j][k],v);
  21. }
  22. int query(int x,int y,int z) {
  23. int ret=INF;
  24. for (int i=x;i;i-=i&-i)
  25. for (int j=y;j;j-=j&-j)
  26. for (int k=z;k;k-=k&-k)
  27. ret=min(ret,bit[i][j][k]);
  28. return ret;
  29. }
  30. }bit[];
  31.  
  32. int main()
  33. {
  34. cin>>n>>m>>h>>T;
  35. for (int i=;i<;i++) bit[i].init(n,m,h); //这里很重要
  36. while (T--) {
  37. int opt,x,y,z; scanf("%d%d%d%d",&opt,&x,&y,&z);
  38. if (opt==) {
  39. for (int i=;i<;i++) {
  40. int v=((i&)?x:-x)+(((i>>)&)?y:-y)+(((i>>)&)?z:-z);
  41. bit[i].update((i&)?n+-x:x,((i>>)&)?m+-y:y,((i>>)&)?h+-z:z,v);
  42. }
  43. } else {
  44. int ans=INF;
  45. for (int i=;i<;i++) {
  46. int v=((i&)?x:-x)+(((i>>)&)?y:-y)+(((i>>)&)?z:-z);
  47. int tmp=bit[i].query((i&)?n+-x:x,((i>>)&)?m+-y:y,((i>>)&)?h+-z:z);
  48. ans=min(ans,-v+tmp);
  49. }
  50. printf("%d\n",ans);
  51. }
  52. }
  53. return ;
  54. }

2019牛客暑期多校训练营(第八场) E I的更多相关文章

  1. 2019牛客暑期多校训练营(第九场)A:Power of Fibonacci(斐波拉契幂次和)

    题意:求Σfi^m%p. zoj上p是1e9+7,牛客是1e9:  对于这两个,分别有不同的做法. 前者利用公式,公式里面有sqrt(5),我们只需要二次剩余求即可.     后者mod=1e9,5才 ...

  2. 2019牛客暑期多校训练营(第一场)A题【单调栈】(补题)

    链接:https://ac.nowcoder.com/acm/contest/881/A来源:牛客网 题目描述 Two arrays u and v each with m distinct elem ...

  3. 2019牛客暑期多校训练营(第一场) B Integration (数学)

    链接:https://ac.nowcoder.com/acm/contest/881/B 来源:牛客网 Integration 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 5242 ...

  4. 2019牛客暑期多校训练营(第一场) A Equivalent Prefixes ( st 表 + 二分+分治)

    链接:https://ac.nowcoder.com/acm/contest/881/A 来源:牛客网 Equivalent Prefixes 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/ ...

  5. 2019牛客暑期多校训练营(第二场)F.Partition problem

    链接:https://ac.nowcoder.com/acm/contest/882/F来源:牛客网 Given 2N people, you need to assign each of them ...

  6. 2019牛客暑期多校训练营(第一场)A Equivalent Prefixes(单调栈/二分+分治)

    链接:https://ac.nowcoder.com/acm/contest/881/A来源:牛客网 Two arrays u and v each with m distinct elements ...

  7. [状态压缩,折半搜索] 2019牛客暑期多校训练营(第九场)Knapsack Cryptosystem

    链接:https://ac.nowcoder.com/acm/contest/889/D来源:牛客网 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 262144K,其他语言52428 ...

  8. 2019牛客暑期多校训练营(第二场)J-Subarray(思维)

    >传送门< 前言 这题我前前后后看了三遍,每次都是把网上相关的博客和通过代码认真看了再思考,然并卵,最后终于第三遍也就是现在终于看懂了,其实懂了之后发现其实没有那么难,但是的的确确需要思维 ...

  9. 2019牛客暑期多校训练营(第一场)-A (单调栈)

    题目链接:https://ac.nowcoder.com/acm/contest/881/A 题意:给定两个长度均为n的数组a和b,求最大的p使得(a1,ap)和(b1,bp)等价,等价的定义为其任意 ...

  10. 2019牛客暑期多校训练营(第一场)A - Equivalent Prefixes(单调栈)

    题意 给定两个$n$个元素的数组$a,b$,它们的前$p$个元素构成的数组是"等价"的,求$p$的最大值."等价"的意思是在其任意一个子区间内的最小值相同. $ ...

随机推荐

  1. solrJ 基本使用

    添加: PropertiesUtils pro = new PropertiesUtils();String path = pro.load("solr.properties", ...

  2. MySQL MHA相关测试

    接上篇文章,介绍了如何安装mysql mha,地址如下:http://blog.csdn.net/yiyuf/article/details/40340895 下面接着进行mha的相关测试: SSH  ...

  3. office2016pro专业版命令行激活

    1.首先查看Office2016安装目录在哪里,如果是默认安装,没有修改路径,是在C:\Program Files\Microsoft Office\Office16目录下,具体路径还得自行查看,我的 ...

  4. 【串线篇】spring boot配置文件大全【上】

    一.配置文件 SpringBoot使用一个全局的配置文件,配置文件名是固定的: • application.properties • application.yml 配置文件的作用:修改SpringB ...

  5. wxpython 文本框TextCtrl

    path_text = wx.TextCtrl(frame, pos=(5, 5), size=(350, 24))最常用的两个函数:path = path_text.GetValue() conte ...

  6. 人生苦短_我用Python_logging日志操作_011

    话不多说,开搞,基础是先使用自带loggin模块,level为warning以上, 进一步是自定义logger,level可自定义 #!/usr/bin/env python # -*- coding ...

  7. yii2.0增删改查实例讲解

    yii2.0增删改查实例讲解一.创建数据库文件. 创建表 CREATE TABLE `resource` ( `id` int(10) NOT NULL AUTO_INCREMENT, `textur ...

  8. 前端自动化-gulp入门

    前不久本人写了一篇关于gulp安装和配置的文章,其实当时还是懵逼的状态,但是今天再次温习了一遍,感觉对整个流程有个整体的理解了,下面以一个实例给大家分享下我的经验供参考和学习. 1.首先安装nodej ...

  9. jenkins实现手动选择分支构建项目-Git Paramater

    先下载插件: Git Paramater 参照: jenkins-参数化构建(三)插件:Git Parameter https://www.cnblogs.com/zhaojingyu/p/98624 ...

  10. 练习1-20 编写程序detab,将输入中的制表符替换成适当数目的空格.

    1.问题描述 编写程序detab,将输入中的制表符替换成适当数目的空格,使空格充满到下一个制表符终止位的地方. 假设制表符终止位的位置是固定的, 换句话说每隔n列就会出现一个制表符终止位. 2.描述 ...