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在同个集合就是连通。然后注意回溯的时候还原并查集,同时因为有回溯操作,并查集不能路径压缩而使用启发式合并。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+;
typedef long long LL;
int n,m,fa[N],sz[N];
LL ans;
struct edge{
int u,v,l,r;
}e[N]; int getfa(int x) { return x==fa[x] ? x : getfa(fa[x]); } vector<int> tree[N<<],b;
void update(int rt,int l,int r,int ql,int qr,int id) {
if (ql<=l && r<=qr) {
tree[rt].push_back(id);
return;
}
int mid=(l+r)>>;
if (ql<=mid) update(rt<<,l,mid,ql,qr,id);
if (qr>mid) update(rt<<|,mid+,r,ql,qr,id);
} void dfs(int rt,int l,int r) {
vector<int> rec; rec.clear();
int mid=(l+r)>>;
for (int i=;i<tree[rt].size();i++) {
int x=e[tree[rt][i]].u,y=e[tree[rt][i]].v;
int fx=getfa(x),fy=getfa(y);
if (sz[fx]>sz[fy]) swap(fx,fy);
rec.push_back(fx);
fa[fx]=fa[fy]; sz[fy]+=sz[fx];
}
if (getfa()==getfa(n)) ans+=b[r]-b[l-]; //每个点代表的是该点到右边一个点的区间值
else if (l<r) dfs(rt<<,l,mid),dfs(rt<<|,mid+,r);
for (int i=;i<rec.size();i++) fa[rec[i]]=rec[i];
} int main()
{
cin>>n>>m;
for (int i=;i<=m;i++) {
scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].r);
b.push_back(e[i].l); b.push_back(e[i].r+); //右端点+1
}
sort(b.begin(),b.end());
b.erase(unique(b.begin(),b.end()),b.end());
for (int i=;i<=m;i++) {
int l=lower_bound(b.begin(),b.end(),e[i].l)-b.begin()+;
int r=lower_bound(b.begin(),b.end(),e[i].r+)-b.begin()+;
update(,,b.size(),l,r-,i); //原区间离散化后是[l,r-1]
} for (int i=;i<=n;i++) fa[i]=i,sz[i]=;
dfs(,,b.size());
cout<<ans<<endl;
return ;
}

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)个也不会太慢。

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

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+;
const int dx[]={-,,,,,};
const int dy[]={,,-,,,};
const int dz[]={,,,,-,};
struct dat{ int x,y,z; };
int n,m,h,T,dis[N];
vector<dat> v; int id(int x,int y,int z) { return (x-)*m*h+(y-)*h+z; } queue<dat> q;
void bfs() {
while (!q.empty()) q.pop();
for (int i=;i<v.size();i++) {
q.push((dat){v[i].x,v[i].y,v[i].z});
dis[id(v[i].x,v[i].y,v[i].z)]=;
}
while (!q.empty()) {
dat u=q.front(); q.pop();
for (int i=;i<;i++) {
int nx=u.x+dx[i],ny=u.y+dy[i],nz=u.z+dz[i];
if (nx< || nx>n || ny< || ny>m || nz< || nz>h) continue;
if (dis[id(nx,ny,nz)]>dis[id(u.x,u.y,u.z)]+) {
dis[id(nx,ny,nz)]=dis[id(u.x,u.y,u.z)]+;
q.push((dat){nx,ny,nz});
}
}
}
} int main()
{
cin>>n>>m>>h>>T;
int k=(int)sqrt(n*m*h);
memset(dis,0x3f,sizeof(dis));
while (T--) {
int opt,x,y,z; scanf("%d%d%d%d",&opt,&x,&y,&z);
if (opt==) {
v.push_back((dat){x,y,z});
if (v.size()>=k) { //定期重构
bfs();
v.clear();
}
} else {
int ans=dis[id(x,y,z)];
for (int i=;i<v.size();i++)
ans=min(ans,abs(x-v[i].x)+abs(y-v[i].y)+abs(z-v[i].z));
printf("%d\n",ans);
}
}
return ;
}

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

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

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

#include<bits/stdc++.h>
using namespace std;
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef vector<VVI> VVVI;
const int INF=0x3f3f3f3f;
int n,m,h,T; struct BIT{
int n,m,h;
VVVI bit;
void init(int _n,int _m,int _h) { //初始化好大小就可以直接当作三维数组使用
n=_n; m=_m; h=_h;
bit=VVVI(n+,VVI(m+,VI(h+,INF)));
}
void update(int x,int y,int z,int v) {
for (int i=x;i<=n;i+=i&-i)
for (int j=y;j<=m;j+=j&-j)
for (int k=z;k<=h;k+=k&-k)
bit[i][j][k]=min(bit[i][j][k],v);
}
int query(int x,int y,int z) {
int ret=INF;
for (int i=x;i;i-=i&-i)
for (int j=y;j;j-=j&-j)
for (int k=z;k;k-=k&-k)
ret=min(ret,bit[i][j][k]);
return ret;
}
}bit[]; int main()
{
cin>>n>>m>>h>>T;
for (int i=;i<;i++) bit[i].init(n,m,h); //这里很重要
while (T--) {
int opt,x,y,z; scanf("%d%d%d%d",&opt,&x,&y,&z);
if (opt==) {
for (int i=;i<;i++) {
int v=((i&)?x:-x)+(((i>>)&)?y:-y)+(((i>>)&)?z:-z);
bit[i].update((i&)?n+-x:x,((i>>)&)?m+-y:y,((i>>)&)?h+-z:z,v);
}
} else {
int ans=INF;
for (int i=;i<;i++) {
int v=((i&)?x:-x)+(((i>>)&)?y:-y)+(((i>>)&)?z:-z);
int tmp=bit[i].query((i&)?n+-x:x,((i>>)&)?m+-y:y,((i>>)&)?h+-z:z);
ans=min(ans,-v+tmp);
}
printf("%d\n",ans);
}
}
return ;
}

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. day20 python异常处理 try except

    day20 python   一.异常处理     1.什么是异常, 常见异常有:         逻辑错误 ''' name Traceback (most recent call last):   ...

  2. ltp-ddt realtime_cpuload_10p 涉及的cpuloadgen交叉编译及安装

    1.下载源码 https://github.com/ptitiano/cpuloadgen/archive/v0.94.tar.gz 解压 tar -zxvf cpuloadgen-0.94.tar. ...

  3. SAP固定资产(FI-AA),一网打尽(转)

    在固定资产模块的边缘,游荡了四五年的时间了,细细数来,固定资产这个熟悉又抽象的东西(熟悉是由于天天接触常常听说,抽象是具体到系统实现上有些复杂并且常遇到问题),好像也没有多少东西. 1) SAP固定资 ...

  4. BZOJ2280 [Poi2011]Plot 二分+倍增+最小圆覆盖

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=2280 https://loj.ac/problem/2159 题解 显然对于一段的 \(q_i ...

  5. golang-练习3

    题目:将输入的字母变成其下一个字母,并且元音字母大写 package main import "fmt" func LetterChanges(str string) string ...

  6. Java中实现线程同步的三种方法

    实现同步的三种方法 多线程共享数据时,会发生线程不安全的情况,多线程共享数据必须同步. 实现同步的三种方法: 使用同步代码块 使用同步方法 使用互斥锁ReetrantLock(更灵活的代码控制) 代码 ...

  7. 使用yum命令报错

    树莓派(Raspberry Pi 3) centos7使用yum命令报错File "/usr/bin/yum", line 30 except KeyboardInterrupt, ...

  8. js插件-图片椭圆轮播效果

    插件效果图: html 代码如下: <div id="container"> <img src="images/cartoon/1.jpg" ...

  9. 在Android中实现一个简易的Http服务器

    最近遇到一个需求需要在App中创建一个Http服务器供供浏览器调用,用了下开源的微型Htpp服务器框架:NanoHttpd,项目地址:https://github.com/NanoHttpd/nano ...

  10. POJ 3525 Most Distant Point from the Sea (半平面交)

    Description The main land of Japan called Honshu is an island surrounded by the sea. In such an isla ...