[BZOJ2738]矩阵乘法(整体二分)

本文介绍了一种使用整体二分法解决特定问题的方法。通过离散化权值,将问题转化为一系列插入和查询操作,利用二分查找确定第k小元素,并通过树状数组进行高效查询。文章详细阐述了解决方案的思路及实现代码。

题目:

我是超链接

题解:

又是一个整体二分了,因为本人的手残导致现在才调出来,具体来说就是
首先把权值离散
将所有的点看成是单个插入操作,所有的询问看成是查询操作
二分第k小的数mid,将插入操作按照权值排序了之后就是在一段区间里
将<=mid的数加入到bit里,然后对于每一个询问在bit中查询
如果不够的话就往大的走,如果小的话就往小的走
注意如果向mid,r走的话要将k减去当前的答案
要加快读卡常才能过Orz

代码:

#include <map>
#include <cstdio>
#include <algorithm>
using namespace std;
const int M=60005;
const int N=505;
const int pf=255025;
struct po{int x,y,val;}zb[pf];
struct hh{int x1,y1,x2,y2,id,k,num;}q[M];
int c[N][N],a[N][N],n,ans[M],s[pf];
map<int,int>hash;
int read()
{
    int x=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
void add(int x,int y,int v)
{
    for (int i=x;i<=n;i+=i&(-i))
      for (int j=y;j<=n;j+=j&(-j)) c[i][j]+=v;
}
int qurry(int x,int y)
{
    int ans=0;
    for (int i=x;i>=1;i-=i&(-i))
      for (int j=y;j>=1;j-=j&(-j))
        ans+=c[i][j];
    return ans;
}
int cmp(hh a,hh b){return a.num<b.num;}
void ef(int l,int r,int a,int b,int x,int y)
{
    if (a>b) return;
    int mid=(l+r)>>1;
    if (l==r)
    {
        for (int i=a;i<=b;i++) ans[q[i].id]=l;
        return;
    }
    int pm=0;
    for (int i=x;i<=y;i++)
      if (zb[i].val<=mid) pm=i,add(zb[i].x,zb[i].y,1);
      else break;
    int pa=0,pb=b-a+1;
    for (int i=a;i<=b;i++)
    {
        int t=qurry(q[i].x2,q[i].y2)-qurry(q[i].x1-1,q[i].y2)-qurry(q[i].x2,q[i].y1-1)+qurry(q[i].x1-1,q[i].y1-1);
        if (t>=q[i].k) q[i].num=++pa;
        else q[i].num=++pb,q[i].k-=t;
    }
    for (int i=x;i<=pm;i++) add(zb[i].x,zb[i].y,-1);
    sort(q+a,q+b+1,cmp);
    ef(l,mid,a,a+pa-1,x,pm);
    ef(mid+1,r,a+pa,b,pm+1,y);
}
int cmp1(po a,po b){return a.val<b.val;}
int main()
{
    int Q;n=read();Q=read();int num=0;
    for (int i=1;i<=n;i++) 
      for (int j=1;j<=n;j++) a[i][j]=read(),s[++num]=a[i][j];
    sort(s+1,s+num+1);
    int ha=unique(s+1,s+num+1)-s-1;
    for (int i=1;i<=ha;i++) hash[s[i]]=i;
    num=0;
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++) zb[++num].val=hash[a[i][j]],zb[num].x=i,zb[num].y=j;
    for (int i=1;i<=Q;i++) q[i].x1=read(),q[i].y1=read(),q[i].x2=read(),q[i].y2=read(),q[i].k=read(),q[i].id=i;
    sort(zb+1,zb+num+1,cmp1);
    ef(1,ha,1,Q,1,num);
    for (int i=1;i<=Q;i++) printf("%d\n",s[ans[i]]);
}
### 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]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值