对于许多的图论题,有些题可能会出现结点要与区间内的每个点都进行建边,但是如果进行遍历建边,那么复杂度可能会被卡到(O(n^2)),此时线段树的骚操作就出来了。
我们知道线段树的每个结点就是一个区间,那么我们就可以把点与区间建边转换为点与线段树上的结点进行连边。
以下是几道线段树优化建边的题目帮助您理解这个思想。
Legacy (CodeForces - 787D)
题目链接
题意
在一个有(n)个星球的宇宙中,你现在在编号为(s)的星球上,有q种移动到其他星球上的方式,大致分为以下三种类型:
- (u)星球到(v)星球,花费为(w);
- (u)星球到([L,R])区间内的某一个星球,花费为(w);
- ([L,R])区间内的某个星球到(u)星球,花费为(w).
现在问你从(s)到每个星球上的最小花费是多少,如果无法达到某个星球那么这个结点的花费输出(-1).
思路
我们用两棵线段树来辅助建边,一棵从下往上进行建边(记为(A)),一棵从上往下建边(B)。
对于第一种类型直接连边即可,第二种类型(u)与(B)上([L,R])这个区间对应的线段树结点连边,第三种类型则是(A)上([L,R])对应的结点与(u)连边。
最后跑最短路即可。
代码实现如下
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pLL;
typedef pair<LL, int> pLi;
typedef pair<int, LL> pil;;
typedef pair<int, int> pii;
typedef unsigned long long uLL;
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) x&(-x)
#define name2str(name) (#name)
#define bug printf("*********
")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define FIN freopen("D://code//in.txt","r",stdin)
#define IO ios::sync_with_stdio(false),cin.tie(0)
const double eps = 1e-8;
const int mod = 1000000007;
const int maxn = 1e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
LL dis[maxn*15];
int n, q, s, cnt, op, u, l, r, w, v;
int pp1[maxn], pp2[maxn], vis[maxn * 15];
struct edge {
int v, w;
};
vector<edge> G[maxn * 15];
struct node {
int l, r, num;
}segtree[5][maxn<<2];
void build(int rt, int l, int r, int op) {
segtree[op][rt].l = l, segtree[op][rt].r = r;
segtree[op][rt].num = ++cnt;
if(l == r) {
if(op == 1) {
pp1[l] = cnt;
} else {
pp2[l] = cnt;
G[pp2[l]].push_back({pp1[l], 0});
}
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid, op); build(rson, mid + 1, r, op);
if(op == 1) {
G[segtree[op][lson].num].push_back({segtree[op][rt].num, 0});
G[segtree[op][rson].num].push_back({segtree[op][rt].num, 0});
} else {
G[segtree[op][rt].num].push_back({segtree[op][lson].num, 0});
G[segtree[op][rt].num].push_back({segtree[op][rson].num, 0});
}
}
void update(int rt, int l, int r, int u, int w, int op) {
if(segtree[op][rt].l == l && segtree[op][rt].r == r) {
if(op == 1) {
G[u].push_back({segtree[2][rt].num, w});
} else {
G[segtree[1][rt].num].push_back({u, w});
}
return;
}
int mid = (segtree[op][rt].l + segtree[op][rt].r) >> 1;
if(r <= mid) update(lson, l, r, u, w, op);
else if(l > mid) update(rson, l, r, u, w, op);
else {
update(lson, l, mid, u, w, op);
update(rson, mid + 1, r, u, w, op);
}
}
void dij(int s) {
for(int i = 1; i <= cnt; ++i) dis[i] = INF;
dis[pp1[s]] = 0;
priority_queue<pLi, vector<pLi>, greater<pLi> > q;
q.push({0, pp1[s]});
while(!q.empty()) {
int u = q.top().second; q.pop();
if(vis[u]) continue;
vis[u] = 1;
int v;
for(int i = 0; i < (int)G[u].size(); ++i) {
v = G[u][i].v;
if(dis[v] > dis[u] + G[u][i].w) {
dis[v] = dis[u] + G[u][i].w;
q.push({dis[v], v});
}
}
}
}
int main(){
#ifndef ONLINE_JUDGE
FIN;
#endif
scanf("%d%d%d", &n, &q, &s);
for(int i = 1; i <= 2; ++i) build(1, 1, n, i);
while(q--) {
scanf("%d", &op);
if(op == 1) {
scanf("%d%d%d", &u, &v, &w);
G[pp1[u]].push_back({pp2[v], w});
} else {
op--;
scanf("%d%d%d%d", &u, &l, &r, &w);
if(op == 1) update(1, l, r, pp1[u], w, op);
else update(1, l, r, pp2[u], w, op);
}
}
dij(s);
for(int i = 1; i <= n; ++i) {
LL tmp = min(dis[pp1[i]], dis[pp2[i]]);
if(tmp == INF) printf("-1 ");
else printf("%lld ", tmp);
}
printf("
");
return 0;
}
Hash Function
题目链接
题目大意
给你(n)个数,要将这(n)个数放进(hash)表中。放置的规则为:将(a_i)进行(hash),假设第(i)个数对应的值为(a_i),那么它对应的(hash)值为(a_i\%n)。如果它的(hash)值对应的位置没有放数,那么就将这个数放到这个位置;否则就往后移,一直到可以放进去为止。现在给你已经放完的(hash)表,问你放数的顺序。
思路
我们对于每个数(a_i),如果它没有放在它对应的(hash)值的位置上,假设当前的位置为(pos),那么易知([a_i\% hash,pos-1])上的数都是在这个数之前放进来的。因此我们可以用线段树进行优化建边然后跑一遍拓扑排序,如果得到的结点数小于原来非(-1)的个数那么就输出(-1),否则输出字典序最小的构造方法(优先队列替换拓扑排序中的队列)。
代码实现如下
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pLL;
typedef pair<LL, int> pLi;
typedef pair<int, LL> piL;;
typedef pair<int, int> pii;
typedef unsigned long long uLL;
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) x&(-x)
#define name2str(name) (#name)
#define bug printf("*********
")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define FIN freopen("in","r",stdin)
#define IO ios::sync_with_stdio(false),cin.tie(0)
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 2e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
int t, n, cnt;
vector<int> v, G[maxn*10];
int a[maxn], vis[maxn*10], in[maxn*10], dd[maxn];
struct node {
int l, r, num;
}segtree[maxn<<2];
void build(int rt, int l, int r) {
segtree[rt].l = l, segtree[rt].r = r;
segtree[rt].num = ++cnt;
in[segtree[rt].num] = 2;
if(l == r) {
in[cnt] = 0;
dd[l] = cnt;
vis[cnt] = l;
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid); build(rson, mid + 1, r);
G[segtree[lson].num].push_back(segtree[rt].num);
G[segtree[rson].num].push_back(segtree[rt].num);
}
void update(int rt, int l, int r, int u) {
if(segtree[rt].l == l && segtree[rt].r == r) {
G[segtree[rt].num].push_back(u);
in[u]++;
return;
}
int mid = (segtree[rt].l + segtree[rt].r) >> 1;
if(r <= mid) update(lson, l, r, u);
else if(l > mid) update(rson, l, r, u);
else {
update(lson, l, mid, u);
update(rson, mid + 1, r, u);
}
}
int main() {
#ifndef ONLINE_JUDGE
FIN;
#endif
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
int num = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
if(a[i] != -1) {
num++;
}
}
if(num == 0) {
printf("
");
continue;
}
cnt = 0;
v.clear();
build(1, 1, n);
priority_queue<pii, vector<pii>, greater<pii> > q;
for(int i = 1; i <= n; ++i) {
if(a[i] == -1) continue;
int t = a[i] % n;
if(t == i - 1) {
q.push({a[i], dd[i]});
} else if(t > i - 1) {
update(1, t + 1, n, dd[i]);
if(i >= 2) update(1, 1, i - 1, dd[i]);
} else {
if(t + 1 <= i - 1) update(1, t + 1, i - 1, dd[i]);
}
}
int u;
while(!q.empty()) {
u = q.top().second; q.pop();
if(vis[u] != 0) {
v.push_back(vis[u]);
}
for(int i = 0; i < (int)G[u].size(); ++i) {
int v = G[u][i];
if(--in[v] == 0) {
if(vis[v]) q.push({a[vis[v]], v});
else q.push({0, v});
}
}
}
if(v.size() == num) {
for(int i = 0; i < (int)v.size(); ++i) {
printf("%d%c", a[v[i]], i == (int)v.size() - 1 ? '
' : ' ');
}
} else {
printf("-1
");
}
for(int i = 0; i <= cnt; ++i) {
vis[i] = in[i] = 0;
G[i].clear();
}
}
return 0;
}