BZOJ5103 [POI2018]Różnorodność

187 篇文章 0 订阅
79 篇文章 0 订阅

标签:扫描线

题目

题目传送门

Description

给定一个n行m列的矩阵,请对于每个长宽均为k的连续子正方形,统计里面出现过的数值的种类数。

Input

第一行包含三个正整数 n,m,k(n,m3000,kmin(n,m)) n , m , k ( n , m ≤ 3000 , k ≤ m i n ( n , m ) )

接下来n行,每行m个正整数 a[i][j](1a[i][j]100000) a [ i ] [ j ] ( 1 ≤ a [ i ] [ j ] ≤ 100000 ) ,表示矩阵中每个位置的数值。

Output

输出一行两个整数M和S。

f(i,j) f ( i , j ) 表示以 (i,j) ( i , j ) 为左上角的正方形内出现过的数值的种类数,则M表示f的最大值,S表示f的总和。

Sample Input

3 5 2
1 5 3 3 3
4 1 3 3 4
4 2 4 4 3

Sample Output

4 20

分析

正方形求并问题,考虑扫描线

因为边长为给定的,所以每个正方形进入和离开扫描线的时候最多只会改变扫描线一个区间的状态

求这个区间的端点,需要在扫描线上查前驱和后继,用线段树维护


这题鬼畜地卡常,然后模仿ccz大佬加入了一些鬼畜的优化才卡过

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#define u32 unsigned int 
#define mem(x,num) memset(x,num,sizeof x)
#define reg(x) for(int i=last[x];i;i=e[i].next)
using namespace std;
inline ll read(){
    ll f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//**********head by yjjr**********
#define ctz(x) __builtin_ctz(x)
#define clz(x) (31^__builtin_clz(x))
#define inf 1e5
const u32 A=-1,maxn=1e5+6,maxm=3e3+6;
u32 bs[maxm][2][maxm/32+6],t0[maxn][4],t1[maxn][maxm/32+6];
int n,m,k,mx,a[maxm][maxm],s[maxm][maxm],pv[maxn];ll sum;
inline void Xor(u32*a,int x){a[x>>5]^=1<<x;}
inline int Get(u32*a,int x){return a[x>>5]>>x&1;}
void cal(int x,int y,int w,int val){
    int L=-inf,R=inf,p1=y>>5,p2=y>>10,p3;u32 v=t1[w][p1]&A<<y;
    if(v)R=p1<<5|ctz(v);
    else{
        v=t0[w][p2]&A<<p1<<1;
        if(v){p3=p2<<5|ctz(v);R=p3<<5|ctz(t1[w][p3]);}
        else rep(i,p2+1,2)if(t0[w][i]){p3=i<<5|ctz(t0[w][i]);R=p3<<5|ctz(t1[w][p3]);break;}
    }
    v=t1[w][p1]&A>>(31^y);
    if(v)L=p1<<5|clz(v);
    else{
        v=t0[w][p2]&A>>(31^p1)>>1;
        if(v){p3=p2<<5|clz(v);L=p3<<5|clz(t1[w][p3]);}
        else dep(i,p2-1,0)if(t0[w][i]){p3=i<<5|clz(t0[w][i]);L=p3<<5|clz(t1[w][p3]);break;}
    }
    L=max(y,L+k);R=min(min(m+1,y+k),R);
    if(L<R)s[x][L]+=val,s[x][R]-=val;
}
void del(int x,int y,int w){
    Xor(t1[w],y);
    if(!t1[w][y>>5])Xor(t0[w],y>>5);
    cal(x,y,w,-1);
}
void ins(int x,int y,int w){
    cal(x,y,w,1);
    if(!t1[w][y>>5])Xor(t0[w],y>>5);
    Xor(t1[w],y);
}
int main()
{
    n=read(),m=read(),k=read();
    rep(i,1,n)rep(j,1,m)a[i][j]=read();
    rep(j,1,m){
        rep(i,1,n){
            int x=a[i][j],&y=pv[x];
            if(y&&y>i-k)Xor(bs[y][0],j),Xor(bs[i][1],j);
            y=i;
        }
        rep(i,1,n)pv[a[i][j]]=0;
    }
    rep(i,1,n)rep(j,1,m){
        if(i>k&&!Get(bs[i-k][0],j))del(i,j,a[i-k][j]);
        if(!Get(bs[i][1],j))ins(i,j,a[i][j]);
    }
    rep(i,1,n){
        rep(j,1,m)s[i][j]+=s[i][j-1];
        rep(j,1,m)s[i][j]+=s[i-1][j];
        if(i>=k)rep(j,k,m)sum+=s[i][j],mx=max(s[i][j],mx);
    }
    cout<<mx<<' '<<sum<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值