poj3467(维护前缀长度)

链接:点击打开链接

题意:给定一个n*m的矩阵,每个点有一个权值,现在有两种操作,一种是将其中任何一个元素改成1~c中的任意值,另一种是输出权值u按十字计数的种数(具体看样例)

代码:

#include <queue>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m;
int num[105],s[105][105];
int le[105][105],re[105][105],up[105][105],dw[105][105];
void cal(int x,int y,int id){
    int i;
    if(id==1){
        for(i=x+1;i<=n;i++){
            if(s[i][y]==s[x][y])
            dw[x][y]++;
            else
            break;
        }
    }
    else if(id==2){
        for(i=x-1;i>=1;i--){
            if(s[i][y]==s[x][y])
            up[x][y]++;
            else
            break;
        }
    }
    else if(id==3){
        for(i=y-1;i>=1;i--){
            if(s[x][i]==s[x][y])
            le[x][y]++;
            else
            break;
        }
    }
    else{
        for(i=y+1;i<=m;i++){
            if(s[x][i]==s[x][y])
            re[x][y]++;
            else
            break;
        }
    }
}
void update(int x,int y,int op){                //每个点分三种情况讨论一下即可
    int i;
    if(s[x][y]==op)
    return;
    for(i=1;i<=x-1;i++){
        if(dw[i][y]<x-i-1)
        continue;
        else if(dw[i][y]==x-i-1){
            if(s[i][y]==op){
                dw[i][y]++;
                if(x+1<=n&&s[x+1][y]==s[i][y])
                dw[i][y]+=(dw[x+1][y]+1);
            }
        }
        else
        dw[i][y]=x-i-1;
    }
    for(i=n;i>=x+1;i--){
        if(up[i][y]<i-x-1)
        continue;
        else if(up[i][y]==i-x-1){
            if(s[i][y]==op){
                up[i][y]++;
                if(x-1>=1&&s[x-1][y]==s[i][y])
                up[i][y]+=(up[x-1][y]+1);
            }
        }
        else
        up[i][y]=i-x-1;
    }
    for(i=1;i<=y-1;i++){
        if(re[x][i]<y-i-1)
        continue;
        else if(re[x][i]==y-i-1){
            if(s[x][i]==op){
                re[x][i]++;
                if(y+1<=m&&s[x][y+1]==s[x][i])
                re[x][i]+=(re[x][y+1]+1);
            }
        }
        else
        re[x][i]=y-i-1;
    }
    for(i=m;i>=y+1;i--){
        if(le[x][i]<i-y-1)
        continue;
        else if(le[x][i]==i-y-1){
            if(s[x][i]==op){
                le[x][i]++;
                if(y-1>=1&&s[x][y-1]==s[x][i])
                le[x][i]+=(le[x][y-1]+1);
            }
        }
        else
        le[x][i]=i-y-1;
    }
    s[x][y]=op;
    up[x][y]=dw[x][y]=le[x][y]=re[x][y]=0;
    for(i=1;i<=4;i++)
    cal(x,y,i);
}
int main(){
    char ch;
    int c,q,i,j,u,v,id,op;
    while(scanf("%d%d%d%d",&n,&m,&c,&q)!=EOF){
        for(i=1;i<=n;i++)                       //直接维护每个点向四个方向的连续长度
        for(j=1;j<=m;j++)                       //当改任何一个值时,可以每个点直接O(1)
        scanf("%d",&s[i][j]);                   //进行更改,因此复杂度就为O(Q*2*(n+m))
        memset(le,0,sizeof(le));
        memset(re,0,sizeof(re));
        memset(up,0,sizeof(up));
        memset(dw,0,sizeof(dw));
        memset(num,0,sizeof(num));
        for(i=1;i<=n;i++){                      //直接维护出每个点四个方向的值
            for(j=1;j<=m;j++){
                for(id=1;id<=4;id++)
                cal(i,j,id);
            }
        }
        for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)                       //四个方向最小值即为每个点的个数
        num[s[i][j]]+=min(min(up[i][j],dw[i][j]),min(le[i][j],re[i][j]));
        while(q--){
            cin>>ch;
            if(ch=='Q'){
                scanf("%d",&u);
                printf("%d\n",num[u]);
            }
            else{
                scanf("%d%d%d",&u,&v,&op);
                for(j=1;j<=m;j++)               //询问时可以先将原来的值都减掉
                num[s[u][j]]-=min(min(up[u][j],dw[u][j]),min(le[u][j],re[u][j]));
                for(i=1;i<=n;i++){              //当更新后再重新加回来
                    if(i==u)
                    continue;
                    num[s[i][v]]-=min(min(up[i][v],dw[i][v]),min(le[i][v],re[i][v]));
                }
                update(u,v,op);
                for(j=1;j<=m;j++)
                num[s[u][j]]+=min(min(up[u][j],dw[u][j]),min(le[u][j],re[u][j]));
                for(i=1;i<=n;i++){
                    if(i==u)
                    continue;
                    num[s[i][v]]+=min(min(up[i][v],dw[i][v]),min(le[i][v],re[i][v]));
                }
            }
        }
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值