20240926测试

a

题面:
有一个 \(n\times m\)\(01\) 矩阵,求其中 \(1\) 的个数在 \([l,r]\) 的子矩阵数量
题解:
\(f_k\)\(1\) 的个数 \(\le k\) 的子矩阵数量,答案为 \(f_r-f_{l-1}\)
\(n\) 较小,暴力枚举上下区间,在内用双指针维护和小于等于 \(k\) 的段,复杂度 \(\text{O}(n^2m)\)
代码:

#include<cstdio>
#include<bitset>
#define int long long
constexpr int N=35,M=50005;
int n,m,L,R,sum[M],ss[N][M];
char s[M];
std::bitset<M>a[N];
int solve(int x){
    if(x<0)return 0;
    int res=0;
    for(int i=1;i<=n;i++)for(int j=i;j<=n;j++){
        for(int k=1;k<=m;k++)sum[k]=sum[k-1]+ss[j][k]-ss[i-1][k];
        for(int l=1,r=0;l<=m;l++){
            for(;r<m&&sum[r+1]-sum[l-1]<=x;r++);
            res+=r-l+1;
        }
    }
    return res;
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        for(int j=1;j<=m;j++)a[i][j]=s[j]=='1';
    }
    for(int j=1;j<=m;j++)for(int i=1;i<=n;i++)ss[i][j]=ss[i-1][j]+(int)a[i][j];
    scanf("%lld%lld",&L,&R),printf("%lld\n",solve(R)-solve(L-1));
    return fflush(stdout),fclose(stdin),fclose(stdout),0;
}

b

题面:
有一个 \(n\times m\) 的矩阵 \(a_{n,m}\) 每一行不选或者选一个数,至少选一个数,求所有选数方案的选出数的 \(\gcd\) 的总和。
题解:
\(cnt_{i,j}\) 表示第 \(i\)\(j\) 的倍数的出现次数,筛出质数后用类似高维前缀和的方法求。
\(f_i\) 表示 \(\gcd\)\(i\) 的总情况数。

\[f_i=\prod_{j=1}^n(cnt_{j,i}+1)-1-\sum_{j|i\wedge j\ne i}f_j \]

直接计算即可,复杂度 \(\text{O}(nA\log\log A+A\log A)\)
代码:

#include<cstdio>
#define Max(x,y) ((x)<(y)&&((x)=(y)))
#define Add(x,y) (((x)+=(y))>=mod&&((x)-=mod))
#define mul(x,y) ((x)*(y)%mod)
#define add(x,y) ((x)+(y)>=mod?(x)+(y)-mod:(x)+(y))
#define sub(x,y) (add((x),mod-(y)))
#define Mul(x,y) ((x)=mul((x),(y)))
template<typename T>inline void read(T&x){
    x=0;
    char c=getchar();
    for(;c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
typedef long long ll;
const int N=25,M=100005,A=1000005,INF=0x3f3f3f3f;
const ll mod=1000000007;
int a[N][M],n,m,maxa,minp[A],p[A],cntp;
ll cnt[N][A],f[N],ans;
bool isp[A];
int main(){
    read(n),read(m);
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)read(a[i][j]),cnt[i][a[i][j]]++,Max(maxa,a[i][j]);
    for(int i=2;i<=maxa;i++)isp[i]=1;
    for(int i=2;i<=maxa;i++)if(isp[i]){
        p[++cntp]=i;
        for(int j=i<<1;j<=maxa;j+=i)isp[j]=0;
    }
    for(int i=1;i<=n;i++)for(int j=1;j<=cntp;j++)for(int k=maxa/p[j];k;k--)cnt[i][k]+=cnt[i][p[j]*k];
    for(int i=maxa;i;i--){
        int pro=1,sum=1;
        for(int j=1;j<=n;j++)Mul(pro,add(cnt[j][i],1));
        for(int j=i<<1;j<=maxa;j+=i)Add(sum,f[j]);
        f[i]=sub(pro,sum);
    }
    int tmp=1;
    for(int i=1;i<=20;i++)Mul(tmp,7);
    for(int i=1;i<=maxa;i++)Add(ans,mul(f[i],(ll)i));
    printf("%lld\n",ans);
    return fflush(stdout),fclose(stdin),fclose(stdout),0;
}

c

题面:
有一个 \(A\times B\times C\) 的立方体,操作 \(1\) 删掉 \(x\le a\wedge y\le b\) 的部分,操作 \(2\) 删掉 \(y\le a\wedge z\le b\) 的部分,操作 \(3\) 删掉 \(z\le a\wedge x\le b\) 的部分,求一共删了多少。
题解:
求立方体体积并,可以发现垂直 \(x\) 轴的截面的图形一定是类似于楼梯的形状,且随着 \(x\) 的减小,楼梯变大。
先算出操作 \(2\) 的矩形面积,可以通过单调队列维护。
操作2
接下来将操作 \(1\) 和操作 \(3\)\(x\) 从大到小排序,然后扫描线,操作 \(1\) 和操作 \(3\) 相当于将双端队列的头和尾移动并修改面积。红色部分相当于操作 \(1\),操作 \(3\) 类似。
操作1
应为每个点最多进队出队一次,加上排序时间复杂度 \(\text O(n\log n)\)
代码

#include<cstdio>
#include<algorithm>
#include<vector>
template<typename T>inline void read(T&x){
    x=0;
    char c=getchar();
    for(;c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
typedef long long ll;
const int N=300005;
int n,cntq1,cntq2,head=1,tail;
ll A,B,C,ans,S;
struct Que1{
    ll y,z;
    bool operator<(const Que1&b){
        return y^b.y?y<b.y:z<b.z;
    }
}q1[N],a[N],stk[N];
struct Que2{
    int type;
    ll x,y,z;
    bool operator<(const Que2&b){
        return x>b.x;
    }
}q2[N];
int main(){
    read(n),read(A),read(B),read(C);
    for(int i=1;i<=n;i++){
        int type;
        ll a,b;
        read(type),read(a),read(b);
        switch(type){
            case 1:
            q2[++cntq2]={type,a,b,C};
            break;
            case 2:
            q1[++cntq1]={a,b};
            break;
            case 3:
            q2[++cntq2]={type,b,B,a};
        }
    }
    std::sort(q1+1,q1+cntq1+1),std::sort(q2+1,q2+cntq2+1);
    for(int i=1;i<=cntq1;i++){
        for(;tail&&a[tail].z<=q1[i].z;)tail--;
        a[++tail]=q1[i];
    }
    for(int i=1;i<=tail;i++)S+=a[i].z*(a[i].y-a[i-1].y);
    q2[0].x=A;
    for(int i=1;i<=cntq2;i++){
        ans+=S*(q2[i-1].x-q2[i].x);
        if(q2[i].type==1){
            int top=0;
            for(;head<=tail&&q2[i].y>=a[head].y;)stk[++top]=a[head],head++;
            if(head<=tail)stk[++top]={q2[i].y,a[head].z};
            a[--head]={q2[i].y,q2[i].z};
            ll SS=0;
            for(int i=1;i<=top;i++)SS+=stk[i].z*(stk[i].y-stk[i-1].y);
            S+=q2[i].y*q2[i].z-SS;
        }
        else{
            int top=0;
            for(;head<=tail&&q2[i].z>=a[tail].z;)stk[++top]=a[tail],tail--;
            if(head<=tail)stk[++top]={a[tail].y,q2[i].z};
            a[++tail]={q2[i].y,q2[i].z};
            ll SS=0;
            std::reverse(stk+1,stk+top+1);
            for(int i=1;i<=top;i++)SS+=stk[i].z*(stk[i].y-stk[i-1].y);
            S+=q2[i].y*q2[i].z-SS;
        }
    }
    ans+=S*q2[cntq2].x,printf("%lld\n",ans);
    return fflush(stdout),fclose(stdin),fclose(stdout),0;
}
posted @ 2024-09-26 16:09  junjunccc  阅读(5)  评论(0编辑  收藏  举报