题目链接
题意:
给你一个(2^n)*(2^n)的矩阵,矩阵元素只有0,1两种颜色。
定义一个元素的价值是
1.如果一个矩阵都是一种颜色,那么他的价值为1
2.如果一个矩阵不纯色,那么他的价值是把他分成4个(2^(k-1))*(2^(k-1))
(假设原来的大小是(2^k)*(2^k))的矩阵的价值之和+1
然后有q个操作,一开始矩阵都是0颜色,
每一次操作,你需要把t=0,把第x行都取反;t=1,把第x列取反。
然后输出当前矩阵的价值
(n<=20,q<=1e6)
解析:
这道题真的磨了2天......
一开始想了一天,想不出来,看了别人的题解
大佬题解
结论:我们称依题目给出的公式计算矩阵A的得分时递归处理到的矩阵均称为A的“子矩阵”,记这些矩阵中有n个为不“纯色”的矩阵,则矩阵A的得分为4n+1。
那么我们然后就只要找(2^n)*(2^n)的里面有多少不纯色的矩阵就可以了。
然后剩下的就是一些套路了...(反正我是看不出来,模仿大佬的口气)
因为他这里行数达到2^n次,并且每一次都对行/列进行操作,那么我们的突破点也一定是对行/列进行的。
然后注意纯色矩阵是有一些性质的:
一个矩阵是纯色矩阵,那么里面所有行的操作次数的奇偶性一定是一样的,并且列也满足这一条件
那么我们只要找到纯色矩阵的数目,在用所有矩阵的数目-纯色=非纯色
然后假定我们要找(2^k)*(2^k)大小的纯色矩阵个数
其实就是找[1,2^n]行平分成2^(n-k)组长度为2^k的连续行区间,在这些组里面找多少组行的奇偶性相同的,
同理再用相同的方法找出列有多少组,那么两者相乘就是(2^n)*(2^n)里面有多少(2^k)*(2^k)大小的纯色矩阵。
求法就是用线段树来维护,因为这里行永远是2^n,所以建出来的线段树永远是满二叉树。
线段树每一个节点代表,该区间内有多少节点颜色为1,那么对于求有多少组长度为2^k的连续行区间奇偶相同,
就只需要判断第n-k层有多少点值为0/2^k
最后更新的时候,同时更新一遍答案数组。这里我又卡了一天。写了超多线段树都T了,
后来看大佬的代码,发现每一次更新,只有线段树上路径上的点的价值会改变,即各层的点只会有一个点答案会+1/-1/不变。
那么在更新线段树的同时,更新这一层的答案就可以了。
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = (1<<20)+100;
int Btree[N*4][2];
//int status[N*8][2];
int bit[31];
ll four[21];
int mat[2][N];
int n;
ll valz[2][N];
void updateone(int root,int l,int r,int index,int val,int rc,int dep)
{
if(l>r)return;
if(l==r)
{
if(l==index)
{
Btree[root][rc]+=val;
}
return;
}
if(Btree[root][rc]==bit[n-dep]||Btree[root][rc]==0)
valz[rc][n-dep]--;
int mid=(l+r)/2;
if(index<=mid)
updateone(root*2,l,mid,index,val,rc,dep+1);
else
updateone(root*2+1,mid+1,r,index,val,rc,dep+1);
Btree[root][rc]=Btree[root*2][rc]+Btree[root*2+1][rc];
if(Btree[root][rc]==bit[n-dep]||Btree[root][rc]==0)
valz[rc][n-dep]++;
}
inline void read(int& x){
char ch = getchar();
x = 0;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x*10+(ch-'0');
}
int main()
{
int q;
read(n),read(q);
bit[0]=1;
ll sum=1;
for(int i=1;i<=n+10;i++) bit[i]=bit[i-1]*2;
ll four=1;
for(int i=1;i<=n;i++)
{
four=four*4;
sum+=four;
}
for(int i=0;i<=n;i++)
valz[0][i]=valz[1][i]=bit[n-i];
for(int i=1;i<=q;i++)
{
int t,x;
read(t),read(x);
int val=1;
if(mat[t][x]==1) val=-1;
mat[t][x]^=1;
ll ans=0;
updateone(1,1,bit[n],x,val,t,0);
for(int k=0;k<=n;k++) //区间长度
{
ans+=valz[t][k]*valz[t^1][k];
}
ans=sum-ans;
printf("%lld\n",4*ans+1);
}
}