bzoj2738: 矩阵乘法

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=2738

题解

  我百度了题解发现是整体二分,点开黄学长的题解看了看,然后就自行脑补了一个整体二分。写的不仅丑而且常数巨大。不太想深入学,因为考到的概率确实不大。
  其实表面上叫做整体二分,实现起来也确实像二分,但算法的实质是分治。
  大致思想就是二分答案,然后看看哪些询问的答案比二分的答案大,哪些小,递归分治下去。
  这道题就可以二分答案 mid ,每次把权值小于等于 mid 的数对应位置上 +1 (可以用二维BIT实现)。然后扫描当前的询问,对于一个询问看它对应的那个子矩形中已经插入了几个数,如果插入的数的个数比询问的 k <script type="math/tex" id="MathJax-Element-161">k</script>大,就说明枚举的答案过大,就把当前询问加入左边,否则加入右边。
  然后分治下去。

代码

//整体二分、二维BIT
#include <cstdio>
#include <algorithm>
#define maxn 510
#define maxq 70000
#define lowbit(x) (x&-x)
using namespace std;
int bit[maxn][maxn], x1[maxq], x2[maxq], y1[maxq], y2[maxq], ans[maxq], w[maxn][maxn],
    N, Q, k[maxq], xu[maxn*maxn], p, x[maxn*maxn], y[maxn*maxn];
void add(int x, int y, int v)
{
    int i, j;
    for(i=x;i<=N;i+=lowbit(i))
        for(j=y;j<=N;j+=lowbit(j))bit[i][j]+=v;
}
int sum(int x, int y)
{
    int ans=0, i, j;
    for(i=x;i;i-=lowbit(i))for(j=y;j;j-=lowbit(j))ans+=bit[i][j];
    return ans;
}
int q(int n)
{return sum(x2[n],y2[n])-sum(x1[n],y2[n])-sum(x2[n],y1[n])+sum(x1[n],y1[n]);}
void bin(int *num, int l, int r)
{
    int i, j, mid=l+r>>1, lq[num[0]+5], rq[num[0]+5];
    if(l==r){for(i=1;i<=num[0];i++)ans[num[i]]=mid;return;}
    while(w[x[xu[p+1]]][y[xu[p+1]]]<=mid)p++,add(x[xu[p]],y[xu[p]],1);
    while(w[x[xu[p]]][y[xu[p]]]>mid and p)add(x[xu[p]],y[xu[p]],-1),p--;
    lq[0]=rq[0]=0;
    for(i=1;i<=num[0];i++)
    {
        if(q(num[i])>=k[num[i]])lq[++lq[0]]=num[i];
        else rq[++rq[0]]=num[i];
    }
    if(lq[0])bin(lq,l,mid);
    if(rq[0])bin(rq,mid+1,r);
}
int read(int x=0)
{
    char c=getchar();
    while(c<48 or c>57)c=getchar();
    while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48, c=getchar();
    return x;
}
bool cmp(int a, int b){return w[x[a]][y[a]]<w[x[b]][y[b]];}
void init()
{
    int i, j, t;
    N=read(), Q=read();
    for(i=1;i<=N;i++)for(j=1;j<=N;j++)w[i][j]=read();
    for(i=1;i<=Q;i++)
        x1[i]=read()-1, y1[i]=read()-1, x2[i]=read(), y2[i]=read(), k[i]=read();
    for(i=1;i<=N;i++)for(j=1;j<=N;j++)
    {
        t=(i-1)*N+j;
        xu[t]=t;
        x[t]=i, y[t]=j;
    }
    w[0][0]=0x7fffffff;
    sort(xu+1,xu+N*N+1,cmp);
}
int main()
{
    int i, num[maxq];
    init();
    for(i=1;i<=Q;i++)num[i]=i;num[0]=Q;
    bin(num,1,1e9);
    for(i=1;i<=Q;i++)printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值