题意:给一个矩阵每个格子里有一个数,现在给T个 子矩阵 和一个 val 值,把子矩阵里不是 val 值的 格子抹去,问最多抹去多少个格子。
题解:
1:先考虑一个特殊的情况:矩形中的数和T次操作放的数都为0或1。 对于这种情况,我们只需要用矩阵前缀和统计一下每个格子被多少个0覆盖,被多少个1覆盖。 如果一个格子的数为0且被放入了至少一个1或这个格子的数位1且被放入了至少一个0则就会对答案产生贡献。
然后考虑原问题。 如果某个格子的数是i,而它被放入了至少一个j,且i!=j,则需要统计进入答案。 注意到,i!=j则i和j至少有一个二进制位不相同。 我们枚举0~19的每一个二进制位,然后把所有数字按照这一位是0还是1划分成两个集合,就变成了上述 特殊情况的问题。一个格子只要至少在某一个二进制位的子问题时被统计进入答案,就加到总答案中去。 复杂度 (nm+T)log(nm)
2:
我们给每种植物花费随机一个[1e6,1e7]内的权值,然后对于每种植物,求覆盖它的所有化肥的权值之和。 如果这个值是这个格子权值的倍数,则有很大概率只有同种化肥覆盖了当前格子,否则没有。
然后我们可以尽量只随机质数(所以权值只弄到1e7内,可以预处理所有质数)。 这样,如果一个格子权值是p,那么正确率就是 (p-1)/p。如果多做几次就几乎没有问题了.
只是看懂了标程:
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<assert.h>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }
typedef unsigned int u32;
typedef long long LL;
typedef unsigned long long ULL;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;
const lod pi=acos(-1);
const int oo=1<<30;
const LL OO=1e18;
const int mod=1e9+7;
const int N=1e6+100;
int qpow(int x,int y) {
int ans=1;
while (y) {
if (y&1) ans=1LL*ans*x%mod;
x=1LL*x*x%mod;y>>=1;
}
return ans;
}
int gi() {
int w=0;bool q=1;char c=getchar();
while ((c<'0'||c>'9') && c!='-') c=getchar();
if (c=='-') q=0,c=getchar();
while (c>='0'&&c <= '9') w=w*10+c-'0',c=getchar();
return q? w:-w;
}
namespace io {
const int L=(1<<21)+1;
char ibuf[L],*iS,*iT,obuf[L],*oS=obuf,*oT=obuf+L-1,c,st[55];int f,tp;
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,L,stdin),(iS==iT?EOF:*iS++)):*iS++)
inline void flush() {
fwrite(obuf,1,oS-obuf,stdout);
oS=obuf;
}
inline void putc(char x) { *oS++=x; if (oS==oT) flush(); }
template<class I> inline void gi(I&x) {
for (f=1,c=gc();c<'0'||c>'9';c=gc()) if (c=='-') f=-1;
for (x=0;c<='9'&&c>='0';c=gc()) x=x*10+(c&15); x*=f;
}
template<class I> inline void print(I x) {
if (!x) putc('0');
if (x<0) putc('-'),x=-x;
while (x) st[++tp]=x%10+'0',x/=10;
while (tp) putc(st[tp--]);
}
inline void gs(char*s, int&l) {
for (c=gc();c<'a'||c>'z';c=gc());
for (l=0;c<='z'&&c>='a';c=gc()) s[l++]=c;
s[l]=0;
}
inline void ps(const char*s) { for (int i=0,n=strlen(s);i<n;i++) putc(s[i]); }
struct IOFLUSHER{ ~IOFLUSHER() { flush(); } } _ioflusher_;
};
using io::putc;
using io::gi;
using io::gs;
using io::ps;
using io::print;
int a[N],s[N],f[N];
#define y1 y_1
int x1[N],y1[N],x2[N],y2[N],tp[N];
int n,m;
bool die[N];
#define id(x,y) (((x)-1)*m+(y))
void add(int k,int *f) {//标记
++f[id(x1[k],y1[k])];
if (x2[k]<n) --f[id(x2[k]+1,y1[k])];
if (y2[k]<m) --f[id(x1[k],y2[k]+1)];
if (x2[k]<n&&y2[k]<m) ++f[id(x2[k]+1,y2[k]+1)];
}
void work(int *s) {// 前缀和
int i,j,t;
for (i=t=1;i<=n;i++)
for (j=1;j<=m;j++,t++) {
if (i>1) s[t]+=s[t-m];
if (j>1) s[t]+=s[t-1];
if (i>1&&j>1) s[t]-=s[t-m-1];
}
}
/*
可以计算 一个矩阵 每次对 子矩阵里的数同时操作 后 最后 每个元素的值为多少。
*/
int main()
{
int i,v,T;
gi(n),gi(m),gi(T);
for (i=1;i<=n*m;i++)
gi(a[i]);
for (i=1;i<=T;i++) {
gi(x1[i]),gi(y1[i]),gi(x2[i]),gi(y2[i]),gi(tp[i]);
add(i,s);
}
work(s);// 计算每个格子 被操作了多少次
for (v=0;v<20;v++) {
for (i=1;i<=n*m;i++) f[i]=0;
for (i=1;i<=T;i++)
if (tp[i]>>v&1) {
add(i,f);
}
work(f);// 计算每个格子 被 v位是1的操作 次数
for (i=1;i<=n*m;i++)
if (a[i]>>v&1) {
if (f[i]<s[i]) {// 说明 这个格子被 v位是 0 的操作过
die[i]=true;
}
} else {
if (f[i]) {// 说明 这个格子被 v位是 1 的操作过
die[i]=true;
}
}
}
v=0;
for (i=1;i<=n*m;i++) {
v+=die[i];
}
print(v);putc('\n');
return 0;
}
方法二:多式几次:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<time.h>
#define id(x,y) (((x)-1)*m+(y))
using namespace std;
const int MAXN=20000000;
const int maxn = 1000100;
int prime[MAXN+1];
void getPrime()
{
memset(prime,0,sizeof(prime));
for(int i=2; i<=MAXN; i++)
{
if(!prime[i])prime[++prime[0]]=i;
for(int j=1; j<=prime[0]&&prime[j]<=MAXN/i; j++)
{
prime[prime[j]*i]=1;
if(i%prime[j]==0) break;
}
}
}
int x1[maxn],Y1[maxn],x2[maxn],y2[maxn],tp[maxn];
int n,m,T,pos[maxn],a[maxn],f[maxn];
void add(int k,int *f,int add) {//标记
f[id(x1[k],Y1[k])]+=add;
if (x2[k]<n) f[id(x2[k]+1,Y1[k])]-=add;
if (y2[k]<m) f[id(x1[k],y2[k]+1)]-=add;
if (x2[k]<n&&y2[k]<m) f[id(x2[k]+1,y2[k]+1)]+=add;
}
void work(int *s) {// 前缀和
int i,j,t;
for (i=t=1;i<=n;i++)
for (j=1;j<=m;j++,t++) {
if (i>1) s[t]+=s[t-m];
if (j>1) s[t]+=s[t-1];
if (i>1&&j>1) s[t]-=s[t-m-1];
}
}
int main()
{
getPrime();
// printf("%d\n",prime[0]);
scanf("%d %d %d",&n,&m,&T);
for(int i=1,t=1;i<=n;i++){
for(int j=1;j<=m;j++,t++){
scanf("%d",&a[t]);
pos[t] = prime[rand()%prime[0]+1];
}
}
for(int i=1;i<=T;i++)
{
int cal;
scanf("%d %d %d %d %d",&x1[i],&Y1[i],&x2[i],&y2[i],&cal);
add(i,f,pos[cal]);
}
work(f);
int ans = 0;
for(int i=1;i<=n*m;i++)
{
if(f[i]%(pos[a[i]])) ans++;
}
printf("%d\n",ans);
return 0;
}