2016-12-27 spoj MINSUB 二分,单调栈 spoj INTSUB 思维

每日链接题解

spoj  MINSUB   

题意:给定一个由非负数组成的矩阵M,和一个整数K,对于矩阵M的子矩阵M’,定义min(M’)为M'矩阵中元素的最小值。我们需要找出这样一个子矩阵,该矩阵的面积至少为K,且min(M’)最大化。面积的定义为该矩阵的行数*列数。

tags: 好题      首先想到二分最小值mx,但check(mx)该怎么写呢? 可以简化为01矩阵,然后check(mx)就是要求出最大的全1子矩阵的面积,这个最大全1子矩阵面积(1000*1000)怎么求呢?这里是关键。  先统计出每个位置向左最多有多少个连续的1,然后对于每一列,就是要求出每列中以每个数为最小数的区间,类似于poj 2796

这类问题都是可以二分答案的。把小于二分值的位置设为0,其他设为1,那么问题就变成了求全为1的子矩阵的最大面积,这件事情可以用单调栈搞(方法类似于传送门,事先统计出每个位置向左有多少个1)。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 1005;

int n, m, k, a[N][N], b[N][N], top;
struct Point {int ai, l, r; }sta[N*2];
int calc(int cj)
{
    top=0;
    int ans=0, l;
    sta[++top]=(Point){b[1][cj], 1, 1 };
    rep(i,2,n)
    {
        l=i;
        while(top>0 && sta[top].ai>=b[i][cj]) {
            if(top-1>0) sta[top-1].r=sta[top].r;
            int area= sta[top].ai*(sta[top].r-sta[top].l+1);
            ans=max(ans, area);
            l=sta[top].l;
            --top;
        }
        sta[++top]=(Point){b[i][cj], l, i };
    }
    while(top>0) {
        if(top-1>0) sta[top-1].r=sta[top].r;
        int area= sta[top].ai*(sta[top].r-sta[top].l+1);
        ans=max(ans, area);
        --top;
    }
    return ans;
}
int check(int x)
{
    rep(i,1,n)  rep(j,1,m)
        if(a[i][j]>=x) b[i][j]=b[i][j-1]+1;
        else b[i][j]=0;
    int ans=0;
    rep(j,1,m) ans=max(ans, calc(j));
    return ans;
}
int main()
{
    int T;  scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d %d", &n, &m, &k);
        rep(i,1,n) rep(j,1,m) scanf("%d", &a[i][j]);
        int l=0, r=1e9, ans1, mid;
        while(l<=r) {
            mid=(l+r)>>1;
            if(check(mid)>=k) l=mid+1, ans1=mid;
            else  r=mid-1;
        }
        printf("%d %d\n", ans1, check(ans1));
    }

    return 0;
}
View Code

spoj  INTSUB

题意:给定一个集合,该集合由1,2,3....2n组成,n是一个整数。问该集合中有趣子集的数目,答案mod1e9+7。x的子集合有趣定义为,该子集中至少有两个数,a和b,b是a的倍数且a是集合中最小的元素。

tags:一开始真没想到枚举最小元素,MDZZ

枚举子集中最小的元素,然后确定其他的元素。   假设现在最小元素为a,则有2n/a-1个大于a的元素是a的倍数,且这些元素必须在子集中出现至少一个,剩下的大于a的数取和不取对答案不造成影响。    累计不同的a对答案的贡献即可。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 200005,  mod=1000000007;

ll  fpow(ll a, int b) {ll ans=1; for(; b; a=a*a%mod, b>>=1) if(b&1) ans=ans*a%mod; return ans; }
int main()
{
    int T;  scanf("%d", &T);
    rep(cas,1,T)
    {
        int n;  scanf("%d", &n);
        ll ans=0;
        rep(i,1,2*n) {
            int x=2*n/i-1, y=2*n-i-x;
            ans = (ans+ (fpow(2,x)-1)*fpow(2,y)%mod);
        }
        printf("Case %d: %lld\n", cas, (ans+mod)%mod);
    }

    return 0;
}
View Code
posted @ 2017-05-09 15:56  v9fly  阅读(193)  评论(0编辑  收藏  举报