离线操作及应用
离线操作:读入所有的操作数据,然后一次性处理。
在线操作:每读入一个操作数据,就进行一次操作。
值得注意的是两者并不等价,有的时候,离线操作要比在线操作要快。
但是,离线操作的缺点也非常明显。那就是要占用一些额外的空间。
我们可以这样去理解。
我们的程序需要和输入端连线才能读取数据,如果我们一次性全部读完,我们就可以断开连线,然后操作,这就是离线操作。而如果我们每读入一个数据,就处理一次,那么我们后面肯定还有没读取的数据,所以我们不能断开连线,就要在线操作。
这样就可以很好地理解了
即,当算法时间复杂度过高且没有办法优化时(一般是有很多询问需要大量重复计算),可以通过创建数组进行离线操作,将每一次操作的答案保存在数组当中,注意离散操作往往会伴随着数据的有序性,例如下题,就需要排序确保数据有序性,避免需要回退之类的操作。
例题:
P4185 [USACO18JAN]MooTube G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
相关系数(相关性 ):任意一对视频的相关性定义为沿此路径的任何连接的最小相关性
输入:
有n个视频,m个询问
接下来有n-1行,每行表示视频p和视频q有相关性,且相关系数为r
接下来m行,每行一个询问,当相关系数为k时,与第v个视频相关性大于k的视频数
输出:
每行一个整数表示视频数
AC代码
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100007;
int pre[maxn],depth[maxn];
int ans[maxn]; //ans用来保存离线操作下的结果
struct node
{
int l,r,dis; //视频l和视频r的相关性为dis
node () {}
node(int x,int y,int v) : l(x),r(y),dis(v) {}
}a[maxn*40]; //struct开大一点,一般乘40,不然会及怪的运行错误
struct kkk
{
int v,pot; //v,pot对应每一次询问的相关性界值和视频号
int pos; //每一次询问的编号
}b[maxn*40];
bool cmp1(node x,node y)
{
return x.dis > y.dis;
}
bool cmp2(kkk x,kkk y)
{
return x.v > y.v;
}
int find(int x)
{
if(pre[x] == x)
return x;
return pre[x] = find(pre[x]);
}
void unite(int x,int y)
{
int fx = find(x);
int fy = find(y);
if(fx != fy)
{
pre[fx] = fy;
depth[fy]+= depth[fx];
}
}
int main()
{
int n,m;
//输入数据初始化
cin >> n >> m;
for(int i=1;i<=n;i++)
{
pre[i] = i;
depth[i] = 1;
}
for(int i=1;i<n;i++)
cin >> a[i].l >> a[i].r >> a[i].dis;
for(int i=1;i<=m;i++)
{
cin >> b[i].v >> b[i].pot;
b[i].pos = i;
}
/* 对结构体排序,让每一次询问都从大到小询问,这样就可以避免先询问小的,后续询问大的,
* 导致前面已经联合的小的视频无法去除,也保证可以按照顺序联合将相关系数大于该询问的两个视频联合
* (联合视频的结构体已经按相关系数排序)
* 从而避免了多次查找,但因为排序会打乱顺序,所以要对查找结构体添加编号pos,
*/
sort(a+1,a+1+n,cmp1);
sort(b+1,b+1+m,cmp2);
//
// cout << "测试" << endl;
// for(int i=1;i<n;i++)
// cout << a[i].l << " " << a[i].r << " " << a[i].dis << endl;
// for(int i=1;i<=m;i++)
// cout << b[i].v << " " << b[i].pot << endl;
// cout << "测试" << endl;
//
//由相关系数从大到小便利每一次询问
int cut = 1; //便利保存相关关系的结构体 !!这里只需要便利一次!!
for(int i=1;i<=m;i++)
{
int d = b[i].v; //保存这次询问的相关性大小
int p = b[i].pot; //保存这次询问的视频节点pot
int pos = b[i].pos; //记录编号用于保存ans
while(a[cut].dis >= d && cut < n)
{
unite(a[cut].l,a[cut].r);
cut++;
}
ans[pos] = depth[find(p)] - 1;
}
//输出
for(int i=1;i<=m;i++)
cout << ans[i] << endl; //深度要减一,减去自己
return 0;
}