zoukankan      html  css  js  c++  java
  • [GXOI/GZOI2019]与或和[单调栈]

    也许更好的阅读体验

    (mathcal{Description})

    给出一个 (n imes n) 的, 元素为自然数的矩阵.
    这个矩阵有许许多多个子矩阵, 定义它的所有子矩阵形成的集合为 (S) .
    对于一个矩阵 (k) , 定义 (f (k))(k) 中所有元素的 (AND) 值 (按位与).
    对于一个矩阵 (k) , 定义 (g(k))(k) 中所有元素的 (OR) 值 (按位或).
    请求出所有子矩阵的 (f (k)) 之和与所有子矩阵的 (g(k)) 之和, 即 (egin{aligned}sum_{kin S} f (k)end{aligned})(egin{aligned}sum_{kin S} g (k)end{aligned})
    由于答案可能很大, 只需要输出答案对(1e9+7)取模的结果.

    (nleq1000,)矩阵中每个元素大小在(int)范围内

    (mathcal{Solution})

    对于有位运算操作的题目,计算贡献时首先考虑的方法就是按位计算贡献
    因为某一位的贡献是不会受到其他位影响的,而整体计算因为位运算的特殊性,还是会考虑到按位计算

    我们枚举每一位(k),然后把矩阵换成(0/1)矩阵,其中(0)元素表示原本矩阵中这一元素在枚举到的这位二进制表示下为(0),为(1)则为(1)
    考虑这个(0/1)矩阵中有多少个子矩阵会被计算贡献,最后把这个数乘以(2^k)

    对于与运算,就是要求里面有多少个全(1)矩阵
    对于或运算,就是要求里面有多少个有(1)矩阵

    与运算和或运算实际上可以互相转换
    (1)矩阵数=所有矩阵数-有(0)矩阵数
    (1)矩阵数=所有矩阵数-全(0)矩阵数

    (all)(n imes n)的矩阵所包含的所有子矩阵
    显然(all=(frac{n(n+1)}{2})^2)

    之后考虑怎么计算满足条件的矩阵个数

    (55)分解法 (O(log_2ncdot n^3))
    考虑枚举矩阵的上边界,下边界
    我们计算在这样的矩阵中满足要求的个数
    (1)矩阵显然容易做些
    考虑枚举以一个矩阵中最左边的(1)出现位置
    这样可以不重不漏全部计算
    显然,只要包含了这个有(1)的一列,当前范围内所有矩阵都是合法的
    注意不要和上一个有(1)的那列重复了

    (sum[i][j])表示第(j)列到第(i)个位置共有多少(1)
    只要(sum[下边界][当前列]-sum[上边界-1][当前列]>0)就说明这一列有(1)
    其他的很简单,具体的过会儿看代码吧

    (100)分解法 (O(log_2ncdot n^2))

    考虑枚举矩阵的下边界
    计算全(1)矩阵
    (f[i][j])表示第(j)列,从(i)开始往上有多少个连续的(1)
    然后枚举到哪一列了,用(cnt)表示每次会增加多少个矩阵
    用单调栈维护一下即可

    (mathcal{Code})

    (55)
    试图卡常碰运气版

    /*******************************
    Author:Morning_Glory
    LANG:C++
    Created Time:2019年09月10日 星期二 14时17分44秒
    *******************************/
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #define rint register int
    using namespace std;
    const int maxn = 1003;
    const int mod = 1000000007;
    //{{{cin
    struct IO{
    	template<typename T>
    	IO & operator>>(T&res){
    		res=0;
    		bool flag=false;
    		char ch;
    		while((ch=getchar())>'9'||ch<'0')	flag|=ch=='-';
    		while(ch>='0'&&ch<='9')	res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
    		if (flag)	res=~res+1;
    		return *this;
    	}
    }cin;
    //}}}
    int n,mx,all,ans1,ans2;
    int lt[maxn],up[maxn];
    int a[maxn][maxn],sum[maxn][maxn];
    bool hav[maxn];
    bool b[maxn][maxn];
    //{{{calc
    inline int calc (int mi,bool opt)
    {
    	int res=0;
    	for (rint i=1;i<=n;++i)
    		for (int j=1;j<=n;++j){
    			b[i][j]=(a[i][j]&mi);
    			b[i][j]^=opt;
    			sum[i][j]=sum[i-1][j]+b[i][j];
    		}
    	for (rint i=1;i<=n;++i)//上边界
    		for (rint j=i;j<=n;++j){//下边界
    			int last=0;
    			for (rint k=1;k<=n;++k)//第一个1
    				if (sum[j][k]-sum[i-1][k])	res=(res+(k-last)*(n-k+1))%mod,last=k;
    		}
    	return res;
    }
    //}}}
    int main()
    {
    	cin>>n;
    	for (rint i=1;i<=n;++i)
    		for (rint j=1;j<=n;++j)
    			cin>>a[i][j],mx=max(mx,a[i][j]);
    	all=n*(n+1)/2%mod;
    	all=1ll*all*all%mod;
    	for (rint k=0;k<=31;++k){
    		int t=1<<k;
    		if (t>mx)	break;
    		int num=calc(t,1);
    		num=(all-num+mod)%mod;
    		ans1=(ans1+1ll*num*t%mod)%mod;
    		num=calc(1<<k,0);
    		ans2=(ans2+1ll*num*t%mod)%mod;
    	}
    	printf("%d %d
    ",ans1,ans2);
    	return 0;
    }
    

    (100)

    /*******************************
    Author:Morning_Glory
    LANG:C++
    Created Time:2019年09月10日 星期二 14时17分44秒
    *******************************/
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    using namespace std;
    const int maxn = 1003;
    const int mod = 1000000007;
    //{{{cin
    struct IO{
    	template<typename T>
    	IO & operator>>(T&res){
    		res=0;
    		bool flag=false;
    		char ch;
    		while((ch=getchar())>'9'||ch<'0')	flag|=ch=='-';
    		while(ch>='0'&&ch<='9')	res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
    		if (flag)	res=~res+1;
    		return *this;
    	}
    }cin;
    //}}}
    int n,mx,all,ans1,ans2;
    int stk[maxn];
    int a[maxn][maxn],f[maxn][maxn];
    bool b[maxn][maxn];
    //{{{calc
    inline int calc (int mi,bool opt)
    {
    	int res=0;
    	for (int i=1;i<=n;++i)
    		for (int j=1;j<=n;++j){
    			b[i][j]=(a[i][j]&mi);
    			b[i][j]^=opt;
    			f[i][j]=b[i][j]?f[i-1][j]+1:0;
    		}
    	for (int i=1;i<=n;++i){
    		int cnt=0,top=0;
    		for (int j=1;j<=n;++j){
    			cnt+=f[i][j];
    			while (top&&f[i][stk[top]]>f[i][j])	cnt-=(stk[top]-stk[top-1])*(f[i][stk[top]]-f[i][j]),--top;
    			res=(res+cnt)%mod;
    			stk[++top]=j;
    		}
    	}
    	return res;
    }
    //}}}
    int main()
    {
    	cin>>n;
    	for (int i=1;i<=n;++i)
    		for (int j=1;j<=n;++j)
    			cin>>a[i][j],mx=max(mx,a[i][j]);
    	all=n*(n+1)/2%mod;
    	all=1ll*all*all%mod;
    	for (int k=0;k<=31;++k){
    		int t=1<<k;
    		if (t>mx)	break;
    		int num=calc(t,0);
    		ans1=(ans1+1ll*num*t%mod)%mod;
    		num=calc(1<<k,1);
    		num=(all-num+mod)%mod;
    		ans2=(ans2+1ll*num*t%mod)%mod;
    	}
    	printf("%d %d
    ",ans1,ans2);
    	return 0;
    }
    

    如有哪里讲得不是很明白或是有错误,欢迎指正
    如您喜欢的话不妨点个赞收藏一下吧

  • 相关阅读:
    NORDIC BLE MAC ADDR
    dbm和发射功率得对照表
    git切换账号邮箱
    文件编码问题造成的汉字输出乱码问题
    自定义printf 打印函数
    NORDIC 烧录BLE协议栈后不能用JLINK仿真bootloader问题及修改方案
    NORDIC BLE升级
    NORDIC ble RSSI
    NORDIC 错误文件
    NORDIC 协议栈下使用硬件定时器
  • 原文地址:https://www.cnblogs.com/Morning-Glory/p/11506347.html
Copyright © 2011-2022 走看看