• 【题解】【原创题目】薇尔莉特


    【题解】【原创题目】薇尔莉特

    出题人:辰星凌

    验题人:无

    题目传送门:薇尔莉特

    【题目描述】

    给出一个 (n)(m) 列的矩阵(最初全为 (0)),有 (T) 个操作,每次操作选出一个子矩阵,将其中的所有元素都对 (x) 进行一次 (or),求最后的矩阵。

    【分析】

    【Solution #1】

    纯暴力,没有坑点,按照题意模拟一下就可以了。

    时间复杂度:(O(Tn^2))

    分数:(20pt)

    【Code #1】
    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    #define Re register int
    #define LL long long
    using namespace std;
    const int N=503,P=1e9+7;
    int n,m,a,b,c,d,x,T,HYJ,Ans1,Ans2,A[N][N];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    int main(){
    //  freopen("123.txt","r",stdin);
        in(n),in(m),in(T),in(HYJ),Ans2=HYJ;
        while(T--){
            in(a),in(b),in(c),in(d),in(x);
            for(Re i=a;i<=c;++i)
                for(Re j=b;j<=d;++j)
                    A[i][j]|=x;
        }
        for(Re i=1;i<=n;++i)
            for(Re j=1;j<=m;++j)
                (Ans1+=A[i][j])%=P,Ans2^=A[i][j];
        printf("%d %d %d
    ",Ans1,Ans2,(LL)Ans1*Ans2%P);
    }
    

    【Solution #2】

    如果把取“或”换成“异或”就是一个区修单查的二维树状数组板子,可惜 (or) 不满足可减性。

    那如果如果它分为 (log) 个二进制位呢?

    对于任意一个二进制位,都是满足可减性的,这下子可以放心地上 ( ext{BIT}) 了。

    直接开 (log) 棵二维 ( ext{BIT}),分别单独处理所有的二进制位,每次操作暴力拆分 (x) 进行 (log) 次区间修改,最后再对矩阵中所有元素的每一位进行单点查询即可。

    时间复杂度:(O(kTlog^2n+klog^2n)),其中 (k) 最大为 (30)

    注:要卡下常才能过 (Subtask 2)

    分数:(60pt)

    【Code #2】

    (此代码并非完美,还有至少三处优化,但过 (Subtask 2) 足矣)

    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    #define Re register int
    #define LL long long
    using namespace std;
    const int N=503,P=1e9+7;
    int n,m,a,b,c,d,x,T,HYJ,Ans1,Ans2,A[N][N];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    struct QAQ{//二维树状数组
        int C[N][N];
        inline void add_(Re x,Re y,Re z){while(x<=n){Re i=y;while(i<=m)C[x][i]+=z,i+=i&(-i);x+=x&(-x);}}
        inline void change(Re x1,Re y1,Re x2,Re y2){//子矩阵修改
            add_(x1,y1,1);
            add_(x1,y2+1,-1);
            add_(x2+1,y1,-1);
            add_(x2+1,y2+1,1);
        }
        inline int ask(Re x,Re y){//单点查询
            Re ans=0;
            while(x){Re i=y;while(i)ans+=C[x][i],i-=i&(-i);x-=x&(-x);}
            return ans>0;//只要有一次1即可
        }
    }TT[31];
    int main(){
    //  freopen("123.txt","r",stdin);
        in(n),in(m),in(T),in(HYJ),Ans2=HYJ;
        while(T--){
            in(a),in(b),in(c),in(d),in(x);
            for(Re p=0;p<=30;++p)if((x>>p)&1)TT[p].change(a,b,c,d);//分解inf,只有为1的位才调用BIT
        }
        for(Re p=0;p<=30;++p)
            for(Re i=1;i<=n;++i)
                for(Re j=1;j<=m;++j)
                    if(TT[p].ask(i,j))A[i][j]|=(1<<p);//获取最终的A数组
        for(Re i=1;i<=n;++i)
            for(Re j=1;j<=m;++j)
                (Ans1+=A[i][j])%=P,Ans2^=A[i][j];
        printf("%d %d %d
    ",Ans1,Ans2,(LL)Ans1*Ans2%P);
    }
    

    【Solution #3】

    考虑如何优化 (Solution #2)

    依旧是对于每个二进制位单独处理。

    想想“或”还有没有什么其他的性质?

    单次覆盖性!(其实就是自己瞎取的一个名字)

    现在只看一个元素,只要有一次操作中对 (1) 取了 (or),那么以后不管和谁 (or),都依然是 (1)。换句话说,将操作中 (x)(1) 的二进制位视作一张大小为 ((x_2-x_1)*(y_2-y_1)) 的布,无论有多少张布覆盖了这个元素,且无论覆盖了多少次,只要有其中一次盖住了它,那么它最终就一定属于被盖住的部分(即对应的二进制位为 (1))。

    因此,如果我们在一次操作中覆盖了某个元素,那么下一次枚举到这里时就可以直接跳过它(或者说直接删掉该元素),易知每一个元素都只会被覆盖一次,因此对于一个二进制位下的矩阵覆盖总复杂度为 (O(n^2))

    其中“删除元素”可以用并查集实现:对于每一行都开一个并查集表示该行中所有列的覆盖情况,当 ((i,j)) 被覆盖后,把 ((i,j))((i,j+1)) 所在的联通块连起来,然后再枚举该行下一个联通块(即 ((i,j+1)) 所在连通块),大致代码如下:

    /*从x1到x2枚举i*/
    for(Re j=find(y1);j<=y2;j=find(j+1))
        (i,j)=1,merge(j,j+1);
    

    当然,这样子做只优化了对于列的枚举,行的枚举同理,稍作修改即可。

    时间复杂度:(O(kT+n^2alpha^2(n)+kn^2)),其中 (k) 最大为 (30)(一个不需要卡常也能轻松 ( ext{AC}) 的优秀算法)。

    分数:(100pt)

    【Code #3】
    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    #define Re register int
    #define LL long long
    using namespace std;
    const int N=503,P=1e9+7;
    int n,m,a,b,c,d,x,T,HYJ,Ans1,Ans2,A[N][N];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    struct QAQ{
        bool B[N][N];
        struct QWQ{
            int fa[N];
            inline void CL(){for(Re i=0;i<=m+1;++i)fa[i]=i;}//注意初始化要枚举到n+1
            inline int find(Re x){return x==fa[x]?x:fa[x]=find(fa[x]);}
        }f[N];
        inline void CL(){
            for(Re i=1;i<=n;++i)f[i].CL();//第i(1<=i<=n)个f表示第i行中各列的覆盖情况
            for(Re i=1;i<=n+1;++i)f[0].fa[i]=i;//用第0个f表示行的覆盖情况
        }
        inline void change(Re x1,Re y1,Re x2,Re y2){
            for(Re i=f[0].find(x1);i<=x2;i=f[0].find(i+1)){
                for(Re j=f[i].find(y1);j<=y2;j=f[i].find(j+1))
                    B[i][j]=1,f[i].fa[j]=f[i].find(j+1);//第i行第j个被覆盖,去掉它(放到j+1脚下)
                if(f[i].find(1)==m+1)f[0].fa[i]=f[0].find(i+1);
                //如果第i行全部被覆盖(即1到m全部都放到了m+1脚下),则可以将第i行去掉了(放到第i+1行脚下)
            }
        }
    }TT[31];
    int main(){
    //  freopen("123.txt","r",stdin);
        in(n),in(m),in(T),in(HYJ),Ans2=HYJ;
        for(Re p=0;p<=30;++p)TT[p].CL();//初始化
        while(T--){
            in(a),in(b),in(c),in(d),in(x);
            for(Re p=0;p<=30;++p)if((x>>p)&1)TT[p].change(a,b,c,d);//分解inf
        }
        for(Re p=0;p<=30;++p)
            for(Re i=1;i<=n;++i)
                for(Re j=1;j<=m;++j)
                    if(TT[p].B[i][j])A[i][j]|=(1<<p);//获取最终的A数组
        for(Re i=1;i<=n;++i)
            for(Re j=1;j<=m;++j)
                (Ans1+=A[i][j])%=P,Ans2^=A[i][j];
        printf("%d %d %d
    ",Ans1,Ans2,(LL)Ans1*Ans2%P);
    }
    

    【题外话】

    本题实际上是两个知识点的糅合:
    ((1).) 按照二进制位拆分单独处理(见 【题解】( ext{GXOI/GZOI2019}) 机房游记 ( ext{Day1T1})
    ((2).) 冰茶姬优化矩阵覆盖的枚举(见 【杂文】随心一记 小知识第二条)

    (Code #2) 最初第 (4) 个点死活卡不过去,最后将结构体套结构体换成一个单一的结构体就过了(没想到结构体的调用对常数影响这么大)。

    加上样例一共 (12) 个不同的 (HYJ)(12) 个数字 (11) 个梗您知道几个?

  • 相关阅读:
    k8s-存储-volume
    k8s-存储-configmap
    k8s-集群调度
    k8s-常用命令
    k8s-资源限制
    k8s-更改证书时间
    kubeadmin安装k8s
    CCPC-Wannafly Winter Camp Day7 D---二次函数【数论】【构造】
    洛谷P1219 八皇后【dfs】
    2019寒假计数器
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/12242327.html
Copyright © 2020-2023  润新知