bzoj 1414: [ZJOI2009]对称的正方形

传送门

我有效率这种东西吗,那是什么,可以吃吗

先对每行每列跑manacher,然后需要求出每个点可以向四周扩展的最大距离,

考虑单独一行,对于每个i只需求出它前面距它最远的一个j满足i-j+1>=min(rad[j]~rad[i])

这个可以用单调队列维护,维护一个rad值单增的单调队列,同时记录队首最后一个被弹出去的元素的位置,就可以维护了。

那么对于每行每列都正反跑一遍然后每个点取min,即每个格子对它上下左右预计能扩展的最大正方形边长取min即可。

需要注意这种整个补了一些奇怪的东西的时候有些格子是没有贡献的,计算答案的时候不考虑它。

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<ctime>
const int N=2007; 
typedef long long LL;
using namespace std;
int n,m,a[N][N],mx[N][N],rad1[N][N],rad2[N][N];

template<typename T> void read(T &x) {
    T f=1; x=0; char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

int tot,tp[N],rad[N],que[N],ql,qr,last;
void work(int id,int f) {
    tot=(f==1?m:n);
    memset(rad,0,sizeof(rad));
    for(int i=1;i<=tot;i++) tp[i]=(f==1?a[id][i]:a[i][id]);
    for(int i=1,j,k=0;i<=tot;) {
        while(tp[i-k-1]==tp[i+k+1]) k++;
        rad[i]=k;
        for(j=1;j<=k&&rad[i-j]!=rad[i]-j;j++)
            rad[i+j]=min(rad[i-j],rad[i]-j);
        k=max(k-j,0);
        i+=j;
    }
    for(int i=1;i<=tot;i++)
        if(f==1) rad1[id][i]=rad[i];
        else rad2[i][id]=rad[i];

}

void solve(int id,int f) {
    tot=(f==1?m:n);
    for(int i=1;i<=tot;i++) 
        if(f==1) rad[i]=rad2[id][i]; 
        else rad[i]=rad1[i][id];
    ql=1; qr=0; last=0;
    for(int i=1;i<=tot;i++) {
        while(ql<=qr&&rad[que[qr]]>=rad[i]) qr--;
        que[++qr]=i;
        while(ql<=qr&&(i-que[ql])+1>rad[que[ql]]) last=que[ql],ql++;
        int now=0;
        if(ql<=qr&&(i-que[ql])+1<=rad[que[ql]]) 
            now=min(rad[que[ql]],i-last);
        if(f==1) mx[id][i]=min(mx[id][i],now);
        else mx[i][id]=min(mx[i][id],now);
    }
    ql=1; qr=0; last=tot+1;
    for(int i=tot;i>=1;i--) {
        while(ql<=qr&&rad[que[qr]]>=rad[i]) qr--;
        que[++qr]=i;
        while(ql<=qr&&que[ql]-i+1>rad[que[ql]]) last=que[ql],ql++;
        int now=0;
        if(ql<=qr&&(que[ql]-i)+1<=rad[que[ql]]) 
            now=min(rad[que[ql]],last-i);
        if(f==1) mx[id][i]=min(mx[id][i],now);
        else mx[i][id]=min(mx[i][id],now);
    }
}

int main() {
#ifdef DEBUG
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
#endif
    read(n); read(m);
    LL ans=n*m;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=2*m+1;j++) a[i*2-1][j]=-1;
        for(int j=1;j<=m;j++) {
            a[i*2][j*2-1]=-1;
            read(a[i*2][j*2]);
        }
        a[i*2][m*2+1]=-1;
        if(i==n) for(int j=1;j<=2*m+1;j++) a[n*2+1][j]=-1;
    }
    n=2*n+1; m=2*m+1; 
    memset(mx,127,sizeof(mx));
    for(int i=1;i<=n;i++) work(i,1);
    for(int i=1;i<=m;i++) work(i,-1);
    for(int i=1;i<=n;i++) solve(i,1);
    for(int i=1;i<=m;i++) solve(i,-1);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) {
            if(a[i][j]==-1&&(a[i-1][j]!=-1||a[i][j-1]!=-1)) continue;
            if(a[i][j]!=-1&&mx[i][j]%2==0) mx[i][j]--;
            else if(a[i][j]==-1&&mx[i][j]&1) mx[i][j]--; 
            if(mx[i][j]/2)
            ans+=mx[i][j]/2;
        }
    printf("%lld\n",ans);
    return 0;
}
/*
5 5
4 2 4 4 4 
3 1 4 4 3 
3 5 3 3 3 
3 1 5 3 3 
4 2 1 2 4 
*/
View Code

 

posted @ 2018-01-20 22:46  啊宸  阅读(270)  评论(0编辑  收藏  举报