[bzoj4552][Tjoi2016&Heoi2016]排序-二分+线段树

本文介绍了一种使用线段树和二分搜索解决特定问题的方法:在一个给定的排列上执行多次升序或降序操作后找到指定位置的元素值。通过维护线段树来高效地更新和查询区间内的元素状态。

线段树而已;
参考:http://blog.youkuaiyun.com/werkeytom_ftd/article/details/51366237
http://www.cnblogs.com/gengchen/p/6613566.html

题目大意

有一个n的排列,进行m次操作,每次操作是将一个区间升序或降序排序。
请你输出m次操作后第p个位置的值。
二分答案



我们二分答案x,然后就是判断a[p]>=x?
#include <cstdio>
#define init int l = t[k].l, r = t[k].r, mid = (l + r) >> 1
const int maxn = 1e5 + 1e2;
int n, m, a[maxn], lambda, q;
struct seg {
  int l, r, val, cov;
} t[maxn << 4];
struct op {
  int a, b, c;
} o[maxn];
void update(int k) { t[k].val = t[k << 1].val + t[k << 1 | 1].val; }
void build(int k, int l, int r) {
  t[k].l = l, t[k].r = r, t[k].cov = -1;
  if (l == r) {
    t[k].val = a[l] > lambda;
    return;
  }
  int mid = (l + r) >> 1;
  build(k << 1, l, mid);
  build(k << 1 | 1, mid + 1, r);
  update(k);
}
void pushdown(int k) {
  if (t[k].cov != -1) {
    t[k << 1].cov = t[k].cov;
    t[k << 1 | 1].cov = t[k].cov;
    t[k << 1].val = (t[k << 1].r - t[k << 1].l + 1) * (t[k].cov);
    t[k << 1 | 1].val = (t[k << 1 | 1].r - t[k << 1 | 1].l + 1) * (t[k].cov);
    t[k].cov = -1;
  }
  if (t[k].l < t[k].r)
    update(k);
}
int query(int k, int x, int y) {
  init;
  pushdown(k);
  if (x <= l && r <= y)
    return t[k].val;
  int ans = 0;
  if (x <= mid)
    ans += query(k << 1, x, y);
  if (y > mid)
    ans += query(k << 1 | 1, x, y);
  return ans;
}
void modify(int k, int x, int y, int val) {
  init;
  pushdown(k);
  if (x <= l && r <= y) {
    t[k].val = (r - l + 1) * val;
    t[k].cov = val;
    return;
  }
  if (x <= mid)
    modify(k << 1, x, y, val);
  if (y > mid)
    modify(k << 1 | 1, x, y, val);
  update(k);
}
bool check(int x) {
  lambda = x;
  build(1, 1, n);
  for (int i = 1; i <= m; i++) {
    int opt = o[i].a, x = o[i].b, y = o[i].c;
    int tmp = query(1, x, y);
    if (opt == 0) {
      modify(1, x, y - tmp, 0);
      modify(1, y - tmp + 1, y, 1);
    } else {
      modify(1, x, x + tmp - 1, 1);
      modify(1, x + tmp, y, 0);
    }
  }
  return !query(1, q, q);
}
int main() {
#ifndef ONLINE_JUDGE
  freopen("input", "r", stdin);
#endif
  scanf("%d %d", &n, &m);
  for (int i = 1; i <= n; ++i)
    scanf("%d", &a[i]);
  int l = 1, r = n;
  for (int i = 1; i <= m; i++) {
    scanf("%d %d %d", &o[i].a, &o[i].b, &o[i].c);
  }
  scanf("%d", &q);
  while (l < r) {
    int mid = (l + r) >> 1;
    if (check(mid))
      r = mid;
    else
      l = mid + 1;
  }
  printf("%d", r);
}

我的代码又错了..

#include<bits/stdc++.h>

using namespace std;
#define N 100050
#define md ((ll+rr)>>1)
#define ls (i<<1)
#define rs (ls|1)
#define lson ls,ll,md
#define rson rs,md+1,rr
int n,m;
int ans;
int a[N];
int ask[N][3];
int lazy[N*5];
int sum[N*5];
int p;
void ch(){
    for(int i=1;i<=n;++i)printf("%d",sum[i]);puts("");
}
void down(int i,int ll,int rr){
    if(lazy[i]==-1)return;
    sum[ls]=(md-ll+1)*lazy[i];
    sum[rs]=(rr-md)*lazy[i];
    lazy[ls]=lazy[rs]=lazy[i];
    lazy[i]=-1;
}
int query(int i,int ll,int rr,int l,int r){
    if(l==ll&&rr==r){
        return sum[i];
    }
    down(i,ll,rr);
    if(r<=md)return query(ls,ll,md,l,r);
    else if(l>md)return query(rs,md+1,rr,l,r);
    else{
        return query(lson,l,md)+query(rson,md+1,r);
    }
}
void update(int i,int ll,int rr,int l,int r,int val){
    if(l==ll&&rr==r){
        lazy[i]=val;
        sum[i]=(rr-ll+1)*val;
        return ;
    }
    down(i,ll,rr);
    //cout<<md<<endl;
    //printf("%d %d\n %d %d\n",ll,rr,l,r);
    if(r<=md)update(lson,l,r,val);
    else if(l>md)update(rson,l,r,val);
    else update(lson,l,md,val),update(rson,md+1,r,val);
    sum[i]=sum[ls]+sum[rs];
}
bool ok(int mid){
    memset(lazy,-1,sizeof(lazy));
    //ch();
    for(int i=1;i<=n;++i)
        if(a[i]<mid)update(1,1,n,i,i,0);
        else update(1,1,n,i,i,1);
    for(int i=1;i<=m;++i){
        int t=query(1,1,n,ask[i][1],ask[i][2]);
        if(ask[i][0]){
            update(1,1,n,ask[i][1],ask[i][1]+t-1,1);
            update(1,1,n,ask[i][1]+t-1,ask[i][2],0);
        }
        else{
            update(1,1,n,ask[i][1],ask[i][2]-t+1,0);
            update(1,1,n,ask[i][2]-t+1,ask[i][2],1);
        }
    }
    return !query(1,1,n,p,p);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    for(int i=1;i<=m;++i){
        scanf("%d%d%d",&ask[i][0],&ask[i][1],&ask[i][2]);
    }
    int l=0,r=n;
    scanf("%d",&p);
    while(l<=r){
        int mid=(l+r)>>1;
        if(ok(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
}
### BZOJ1728 Two-Headed Cows (双头牛) 的解题思路 #### 题目概述 BZOJ1728 是一道经典的图论问题,题目描述了一群双头牛之间的关系网络。每只双头牛可以看作是一个节点,而它们的关系则构成了边。目标是从这些关系中找出满足特定条件的最大子集。 此问题的核心在于利用 **二分查找** 和 **染色法** 来验证是否存在符合条件的子图结构[^1]。 --- #### 解题核心概念 ##### 1. 图模型构建 该问题可以通过无向图建模,其中每个顶点代表一只双头牛,边表示两只双头牛之间存在某种关联。最终的目标是在这个图中找到最大的独立集合(Independent Set),即任意两个顶点都不相连的一组顶点[^2]。 ##### 2. 二分查找的应用 为了高效求解最大独立集大小 \( k \),采用二分策略来逐步逼近最优解。具体来说,在区间 [0, n] 中通过不断调整上下界寻找可能的最大值 \( k \)[^3]。 ##### 3. 染色法验证可行性 对于当前假设的最大独立集大小 \( mid \),尝试从原图中选取恰好 \( mid \) 个顶点构成候选集合,并检查其是否形成合法的独立集。这一过程通常借助 BFS 或 DFS 实现,同时配合颜色标记技术区分已访问状态以及检测冲突情况[^4]。 以下是基于 Python 的伪代码实现: ```python from collections import deque def bfs_coloring(graph, start_node): queue = deque() color_map = {} # 初始化起点的颜色为 0 color_map[start_node] = 0 queue.append(start_node) while queue: current = queue.popleft() for neighbor in graph[current]: if neighbor not in color_map: # 给邻居分配相反的颜色 color_map[neighbor] = 1 - color_map[current] queue.append(neighbor) elif color_map[neighbor] == color_map[current]: return False # 如果发现相邻节点有相同颜色,则无法完成有效染色 return True def is_possible_to_select_k(graph, nodes_count, target_size): from itertools import combinations all_nodes = list(range(nodes_count)) possible_combinations = combinations(all_nodes, target_size) for subset in possible_combinations: subgraph = {node: [] for node in subset} valid_subset = True for u in subset: for v in graph[u]: if v in subset and v != u: subgraph[u].append(v) # 对子图进行染色测试 colors_used = set() coloring_success = True for node in subset: if node not in colors_used: success = bfs_coloring(subgraph, node) if not success: coloring_success = False break if coloring_success: return True # 找到一个有效的组合即可返回成功标志 return False def binary_search_max_independent_set(graph, total_nodes): low, high = 0, total_nodes best_result = 0 while low <= high: mid = (low + high) // 2 if is_possible_to_select_k(graph, total_nodes, mid): best_result = mid low = mid + 1 else: high = mid - 1 return best_result ``` --- #### 复杂度分析 上述算法的时间复杂度主要取决于以下几个方面: - 枚举所有可能的子集规模:\( O(\binom{n}{k}) \), 其中 \( k \) 表示当前试探的独立集大小。 - 子图构造与染色检验操作:每次调用 `bfs_coloring` 函数需遍历整个子图,最坏情况下时间开销接近线性级别 \( O(k^2) \). 综合来看整体效率较高但仍有优化空间[^5]. --- #### 总结 通过对 BZOJ1728 进行深入剖析可知,合理运用二分加染色的方法能够显著提升解决问题的能力。这种方法不仅适用于本题场景下寻找最大独立集的任务需求,同时也可推广至其他相似类型的 NP 完全难题处理之中[^6]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值