题目:

 

 

 

 

 

 

 

 分析:

前两个点Q=1,写并查集暴力求连通块的个数。3,4个点n=1,考虑把询问离线,用莫队维护。

得分40。。。。

#include<bits/stdc++.h>
using namespace std;
#define N 4000005
#define nn 2005
#define qq 200005
int a[nn][nn],fa[N],id[nn][nn],tmp[N],Q;
int read()
{
    int x=0; int fl=1; char ch=getchar();
    while(ch>'9'||ch<'0') { if(fl=='-') fl=-1; ch=getchar(); }
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
    return x*fl;
}
int find(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=find(fa[x]);
}
void merge(int aa,int b) { int f1=find(aa),f2=find(b); if(f1!=f2) fa[f1]=f2; }
void work2()
{
    int x1,x2,y1,y2,cnt=0;
    x1=read(); y1=read(); x2=read(); y2=read();
    for(int i=x1;i<=x2;++i)
     for(int j=y1;j<=y2;++j)
      id[i][j]=++cnt;
    for(int i=1;i<=cnt;++i) fa[i]=i;
    for(int i=x1;i<=x2;++i)
     for(int j=y1;j<=y2;++j){
         if(a[i][j]==0) continue;
         if(i-1>=x1 && a[i-1][j]) merge(id[i][j],id[i-1][j]);
         if(i+1<=x2 && a[i+1][j]) merge(id[i][j],id[i+1][j]);
         if(j-1>=y1 && a[i][j-1]) merge(id[i][j],id[i][j-1]);
         if(j+1<=y2 && a[i][j+1]) merge(id[i][j],id[i][j+1]);
    }
    int tot=0;
    for(int i=x1;i<=x2;++i)
     for(int j=y1;j<=y2;++j)
      if(a[i][j]) tmp[++tot]=find(id[i][j]);
    sort(tmp+1,tmp+1+tot);
    int num=unique(tmp+1,tmp+1+tot)-tmp-1;
    printf("%d\n",num);
}
struct node{ int l,r,o; } p[qq];
int ans=0,anss[qq];
bool cmp(const node &a,const node &b)
{
    if(a.l==b.l) return a.r<b.r;
    return a.l<b.l;
}
void add(int x,int fl)
{
    if(a[1][x]==0) return ;
    if(a[1][x+fl]==0) ans++; 
}
void del(int x,int fl)
{
    if(a[1][x]==0) return ;
    if(a[1][x+fl]==0) ans--; 
}
void work1()
{
    int xx;
    for(int i=1;i<=Q;++i) xx=read(),p[i].l=read(),xx=read(),p[i].r=read(),p[i].o=i;
    sort(p+1,p+1+Q,cmp);
    int l=0,r=0;
    for(int i=1;i<=Q;++i){
        while(p[i].l<l) add(--l,1);
        while(p[i].l>l) del(l++,1);
        while(p[i].r<r) del(r--,-1);
        while(p[i].r>r) add(++r,-1);
        anss[p[i].o]=ans;
    }
    for(int i=1;i<=Q;i++) printf("%d\n",anss[i]);
}
char s[nn];
int main()
{
    freopen("duty.in","r",stdin);
    freopen("duty.out","w",stdout);
    int n,m;
    n=read(); m=read(); Q=read();
    for(int i=1;i<=n;++i){
        scanf("%s",s);
        for(int j=0;j<=m-1;++j)
        a[i][j+1]=s[j]-'0';
    }
    if(Q==1) work2();
    else work1();
}
/*
3 4 1
1101
0110
1101
2 2 3 3

1 6 5
111001
1 3 1 4
1 1 1 5
1 5 1 6
1 2 1 5
1 3 1 6

1 18 3
111001110010101100
1 5 1 17
1 2 1 17
1 1 1 17
*/
并查集+莫队

 

但是!!!题解说前7个点暴力都可以得70分。。。。(NMQ1e8的复杂度得70分。。。。)

正解:

每个黑色点间最多只有一条边->。多个连通块就是多棵树,即一个森林。所以说连通块的个数=点数-边数

快速求点数->维护前缀和。求边数->同样维护前缀和。

如何维护边的前缀和

在有连边的点之间插一个值,维护那个值的前缀和即可。

注意代码处理细节。

#include<bits/stdc++.h>
using namespace std;
#define N 4005
int a[N][N],sum[N][N],tot[N][N];
int read()
{
    int x=0; int fl=1; char ch=getchar();
    while(ch>'9'||ch<'0') { if(fl=='-') fl=-1; ch=getchar(); }
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
    return x*fl;
}
char s[N];
int main()
{
    freopen("duty.in","r",stdin);
    freopen("duty.out","w",stdout);
    int n,m,Q;
    n=read(); m=read(); Q=read();
    for(int i=1;i<=2*n;i+=2){//奇数行奇数列的是原数组 
        scanf("%s",s);
        for(int j=1;j<=2*m;j+=2)
        a[i][j]=s[j/2]-'0';
    }
    for(int i=1;i<=2*n;i++){
        if(i&1){
            for(int j=2;j<=2*m;j+=2)
            if(a[i][j-1] && a[i][j+1]) a[i][j]=6;//标记为连边 
        }
        else{
            for(int j=1;j<=2*m;j+=2)
            if(a[i-1][j] && a[i+1][j]) a[i][j]=6;
        }
    }
    for(int i=1;i<=2*n;i++)//处理前缀和 
     for(int j=1;j<=2*m;j++)
      sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(a[i][j]==1);
    for(int i=1;i<=2*n;i++)
     for(int j=1;j<=2*m;j++)
      tot[i][j]=tot[i-1][j]+tot[i][j-1]-tot[i-1][j-1]+(a[i][j]==6);
    while(Q--){
        int x1,x2,y1,y2;
        x1=read(); y1=read(); x2=read(); y2=read();
        x1=x1*2-1,x2=x2*2-1,y1=y1*2-1,y2=y2*2-1;
        int ans1=sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
        int ans2=tot[x2][y2]-tot[x2][y1-1]-tot[x1-1][y2]+tot[x1-1][y1-1];
        printf("%d\n",ans1-ans2);//ans=点数-边数 
    }
}
/*
3 4 1
1101
0110
1101
//样例输出来的样子 
1 6 1 0 0 0 1 0
0 0 6 0 0 0 0 0
0 0 1 6 1 0 0 0
0 0 6 0 0 0 0 0
1 6 1 0 0 0 1 0
0 0 0 0 0 0 0 0
*/
正解

 

posted on 2019-08-31 17:24  rua-rua-rua  阅读(165)  评论(0编辑  收藏  举报