您的位置:首页 > 编程语言 > C语言/C++

2015 年 蓝桥杯 A 组 C/C++ 第十题 灾后重建 【最小生成树 + LCA倍增 + 线段树维护区间max】

2018-03-11 15:29 429 查看
交题地址

详细复杂度做法解释

这是hard版本的.

AC Code

const int maxn = 5e4+5;
const int maxm = 2e5 + 5;
int up[maxn][23], maxx[maxn][23];
int deep[maxn], dis[maxn];
int cnt, head[maxn];
int n, m, q;
struct node {
int to, next, w;
}e[maxm<<1];
int fa[maxn], r[maxn];
void init() {
Fill(head,-1); Fill(dis,0);
Fill(up,0); Fill(deep,0);
cnt = 0;
for (int i = 1 ; i <= n ; i ++) {
fa[i] = i;
r[i] = 1;
}
}
int Find(int x) {
return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
bool Un(int x, int y) {
int fx = Find(x);
int fy = Find(y);
if (fx == fy) return false;
if (r[fx] < r[fy]) swap(fx, fy);
fa[fy] = fx;
r[fx] += r[fy];
return true;
}
void add(int u, int v, int w) {
e[cnt] = node{v, head[u], w};
head[u] = cnt++;
}

void dfs(int u,int fa,int d)
{
deep[u] = d + 1;
for(int i = 1 ; i < 20 ; i ++) {
up[u][i] = up[up[u][i-1]][i-1];
maxx[u][i] = max(maxx[up[u][i-1]][i-1], maxx[u][i-1]);
}
for(int i = head[u] ; ~i ; i = e[i].next) {
int to = e[i].to;
if(to == fa) continue;
dis[to] = dis[u] + e[i].w;
up[to][0] = u;
maxx[to][0] = e[i].w;
dfs(to, u, d+1);
}
}
int LCA_BZ(int u,int v)
{
int mx = 0;
if(deep[u] < deep[v]) swap(u,v);
int k = deep[u] - deep[v];
for(int i = 0 ; i < 20 ; i ++) {
if((1<<i) & k) {
mx = max(mx, maxx[u][i]);
u = up[u][i];
}
}
if(u != v) {
for(int i = 19 ; i >= 0 ; i --) {
if(up[u][i] != up[v][i]){
mx = max(mx, maxx[u][i]);
mx = max(mx, maxx[v][i]);
u = up[u][i];
v = up[v][i];
}
}
mx = max(mx, max(maxx[u][0], maxx[v][0]));
u = up[u][0];
}
return mx;
}
struct edge {
int u, v, w;
bool operator < (const edge& _) const {
return w < _.w;
}
}s[maxm];
int a[maxn], ans[maxn*3];
struct Tree {
int tl, tr, maxx;
}tree[maxn<<2];
void pushup(int id)
{
tree[id].maxx = max(tree[id<<1].maxx , tree[id<<1|1].maxx);
}
void build(int id, int l, int r)
{
tree[id].tl = l, tree[id].tr = r;
if(l == r){
tree[id].maxx = a[l];
return ;
}
int mid = (r+l)>>1;
build(id<<1, l, mid);
build(id<<1|1, mid+1, r);
pushup(id);
}
void update(int id, int pos, int val)
{
int l = tree[id].tl, r = tree[id].tr;
if(l == r){
tree[id].maxx = val;
return ;
}
int mid = (l+r) >> 1;
if(pos <= mid) update(id<<1, pos, val);
else update(id<<1|1, pos, val);
pushup(id);
}
int mx;
void query(int id, int ql, int qr) {
int l = tree[id].tl, r = tree[id].tr;
if(ql <= l && r <= qr ){
mx = max(mx,tree[id].maxx);
return ;
}
int mid = (l + r) >> 1;
if(ql <= mid) query(id<<1, ql, qr);
if(qr > mid) query(id<<1|1, ql, qr);
}
struct que {
int l, r, p, c, id;
bool operator < (const que& _) const {
if (p == _.p) return c > _.c;
return p > _.p;
}
int cal_ans() {
int ans = 0;
for (int i = (l-c+p-1)/p*p+c; i+p <= r ; i += p) {
ans = max(ans, LCA_BZ(i, i+p));
}
return ans;
}
void cal_build() {
Fill(a, 0);
for (int i = (!c)?p:c ; i + p <= n ; i += p) {
a[i] = LCA_BZ(i, i+p);
}
// 因为只要是p, c相同的, 我们不会再建线段树,所以要更新到n
// 而不是r. 因为询问的编号都相同, 所以可以直接求区间max.
build(1, 1, n);
}
}qq[maxn*3];
void solve()
{
scanf("%d%d%d", &n, &m, &q);
init();
for (int i = 1 ; i <= m ; i ++) {
scanf("%d%d%d", &s[i].u, &s[i].v, &s[i].w);
}
sort(s+1, s+1+m);
int num = 0;
for (int i = 1 ; i <= m ; i ++) {
if (Un(s[i].u, s[i].v)) {
num++;
add(s[i].u, s[i].v, s[i].w);
add(s[i].v, s[i].u, s[i].w);
}
if (num == n - 1) break;
}
up[1][0] = 1;
maxx[1][0] = 0;
dfs(1, -1, 0);
for (int i = 1 ; i <= q ; i ++) {
scanf("%d%d%d%d", &qq[i].l, &qq[i].r, &qq[i].p, &qq[i].c);
qq[i].id = i;
}
sort(qq+1, qq+1+q); int con = sqrt(n);
for (int i = 1 ; i <= q ; i ++) {
if (qq[i].p > con) ans[qq[i].id] = qq[i].cal_ans();
else {
if (qq[i].p != qq[i-1].p || qq[i].c != qq[i-1].c) {
qq[i].cal_build();
}
mx = 0;
query(1, qq[i].l, qq[i].r-qq[i].p);
ans[qq[i].id] = mx;
}
}
for (int i = 1 ; i <= q ; i ++) {
printf("%d\n", ans[i]);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: