牛客网暑期ACM多校训练营(第二场)J farm

题意:给一个矩阵每个格子里有一个数,现在给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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值