bzoj2527[Poi2011]Meteors 整体二分

解决一个涉及陨石雨收集的问题,使用整体二分法优化算法效率。通过预处理和区间更新,快速确定各国何时能收集到足够陨石。

 

题目描述:

这个星球经常会下陨石雨。BIU已经预测了接下来K场陨石雨的情况。 BIU的第i个成员国希望能够收集Pi单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。 输入: 第一行是两个数N,M。 第二行有M个数,第i个数Oi表示第i段轨道上有第Oi个国家的太空站。 第三行有N个数,第i个数Pi表示第i个国家希望收集的陨石数量。 第四行有一个数K,表示BIU预测了接下来的K场陨石雨。 接下来K行,每行有三个数Li,Ri,Ai,表示第K场陨石雨的发生地点在从Li顺时针到Ri的区间中(如果Li<=Ri,就是Li,Li+1,...,Ri,否则就是Ri,Ri+1,...,m-1,m,1,...,Li),向区间中的每个太空站提供Ai单位的陨石样本。 输出: N行。第i行的数Wi表示第i个国家在第Wi波陨石雨之后能够收集到足够的陨石样本。如果到第K波结束后仍然收集不到,输出NIE。
数据范围: 1<=n,m,k<=3*10^5 1<=Pi<=10^9 1<=Ai<10^9

 

Sample Input

3 5
1 3 2 1 3
10 5 7
3
4 2 4
1 3 1
3 5 2

Sample Output

3
NIE
1

据说这道题可以用斐波那契数列+二分过,

但我菜到只会用整体二分,请求原谅QWQ......

上代码:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define int long long
using namespace std;
const int maxn=300005;
int c[maxn];
int ans[maxn];
int n,m,Q,x,now;
vector<int>to[maxn];
struct query{int l,r,val;}q[maxn];
struct node{int id,k;}a[maxn],tmp[maxn];
inline int lowbit(int x){return x&-x;}
inline void add(int u,int v)
{
    for(int i=u;i<=m;i+=lowbit(i))
      c[i]+=v;
}
inline int getsum(int u)
{
    int sum=0;
    for(int i=u;i>=1;i-=lowbit(i))
      sum+=c[i];
    return sum;
}
inline void Add(int l,int r,int v)
{
    if(l<=r) add(l,v),add(r+1,-v);
    else add(l,v),add(1,v),add(r+1,-v);
}
inline void CDQ(int L,int R,int l,int r)//别看他叫CDQ,那他也是整体二分。
{
    int l1=L,l2=R,mid=(l+r>>1);
    if(l==r)
    {
        fo(i,L,R) ans[a[i].id]=l;
        return;
    }
    while(now<=mid) now++,Add(q[now].l,q[now].r,q[now].val);
    while(now>mid) Add(q[now].l,q[now].r,-q[now].val),now--;
//  fo(i,1,mid) Add(q[i].l,q[i].r,q[i].val);
    fo(i,L,R)
    {
        int sum=0;
        for(int j=0;j<to[a[i].id].size();j++)
        {
            int czy=to[a[i].id][j];
            sum+=getsum(czy);
            if(sum>=a[i].k) break;
        }
        if(sum>=a[i].k) tmp[l1++]=a[i];
        else tmp[l2--]=a[i];
    }
    reverse(tmp+l2+1,tmp+R+1);
    fo(i,L,R) a[i]=tmp[i];
//  fo(i,1,mid) Add(q[i].l,q[i].r,-q[i].val);
    CDQ(L,l1-1,l,mid);
    CDQ(l2+1,R,mid+1,r);
}
signed main()
{
    scanf("%lld%lld",&n,&m);
    fo(i,1,m)
    {
        scanf("%lld",&x);
        to[x].push_back(i);
    }
    fo(i,1,n) a[i].id=i,scanf("%lld",&a[i].k);scanf("%lld",&Q);
    fo(i,1,Q) scanf("%lld%lld%lld",&q[i].l,&q[i].r,&q[i].val);
    Q++,q[Q].l=1,q[Q].r=m,q[Q].val=1e9+7;
    CDQ(1,n,1,Q);
    fo(i,1,n)
      if(ans[i]<Q) printf("%lld\n",ans[i]);
      else puts("NIE");
    return 0;
}

 

### 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]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值