In mathematics you don't understand things, you just get used to them.

标签 data structures 下的文章

Description

Link.

给定一个 $N \times N$ 的矩阵,她希望求出:

  1. 该矩阵的所有子矩阵的 $\texttt{AND}$ 值之和(所有子矩阵 $\texttt{AND}$ 值相加的结果)。
  2. 该矩阵的所有子矩阵的 $\texttt{OR}$ 值之和(所有子矩阵 $\texttt{OR}$ 值相加的结果)。

Solution

对于每一个数的每一位,我们单独拉出来构成 $\log$ 个矩阵。

对于 $\texttt{AND}$,显然只有全为 $1$ 的子矩阵能产生贡献。

对于 $\texttt{OR}$,只有存在 $1$ 的子矩阵才能产生贡献。

那么枚举右下角,单调栈统计。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD=1e9+7;
int n,pre[1010][1010],stk[1010],top,a[1010][1010],b[1010][1010],ans,exans,mx;
void work(int k,int t)
{
    for(int i=1;i<=n;++i)    for(int j=1;j<=n;++j)    b[i][j]=((a[i][j]>>k)&1);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)
        {
            if(b[i][j]==t)    pre[i][j]=pre[i-1][j]+1;
            else    pre[i][j]=0;
        }
    }
    int siz=0;
    for(int i=1;i<=n;++i)
    {
        siz=top=0,stk[1]=stk[0]=0;
        for(int j=1;j<=n;++j)
        {
            while(top&&pre[i][stk[top]]>=pre[i][j])    siz=(siz-LL(pre[i][stk[top]])*(stk[top]-stk[top-1])%MOD+MOD)%MOD,--top;
//            siz=(siz+LL(pre[i][j])*(j-stk[top])%MOD)%MOD,stk[++top]=j;
            siz+=LL(pre[i][j])*(j-stk[top]),stk[++top]=j;
            if(t)    ans=(ans+LL(siz)*(1<<k)%MOD)%MOD;
            else    exans=(exans+(LL(i)*j%MOD-LL(siz))*(1<<k)%MOD+MOD)%MOD;
        }
    }
}
inline char fgc()
{
    static char buf[1<<17],*p=buf,*q=buf;
    return p==q&&(q=buf+fread(p=buf,1,1<<17,stdin),p==q)?EOF:*p++;
}
void read(int &hhh)
{
    int x=0;
    char c=fgc();
    while(!isdigit(c))    c=fgc();
    while(isdigit(c))    x=(x<<3)+(x<<1)+(c^'0'),c=fgc();
    hhh=x;
}
void write(int x,char las='\n')
{
    static int stk[100],top=0;
    do stk[++top]=x%10,x/=10; while(x);
    while(top)    putchar(stk[top--]^'0');
    putchar(las);
}
int main()
{
    read(n);
    for(int i=1;i<=n;++i)    for(int j=1;j<=n;++j)    read(a[i][j]),mx=max(mx,a[i][j]);
    for(int k=0;(1ll<<k)<=mx;++k)    work(k,0),work(k,1);
    write(ans,' '),write(exans);
    return 0;
}

Description

Link.

给出一个数列,要求将其分成几段,使每段的和非严格递增,求最小的每段的和的平方和。

Solution

注意到 $a_{i}$ 为正,对于正数有这样的结论:

$$ a^{2}+b^{2}<(a+b)^{2} $$

正确性显然。这告诉我们,分的段越多越好。

现在我们来考虑如何使每段的和非严格递增。

考虑暴力 DP,设 $f_{i,j}$ 为前 $i$ 个数然后上一个 breakpoint 是 $j$​ 的最小平方和,转移:

$$ f_{i,j}=\min_{1\le k<j,pre_{i}-pre_{j}\ge pre_{j}-pre_{k}}\{f_{j,k}+(pre_{i}-pre_{j})^{2}\} $$

$pre$ 为前缀和。当然这个 DP 什么结论都没用,没有任何技术含量。

所以我们考虑优化。

你会发现这个 $k$ 不是每次都要重头枚举,他是单调的,于是你维护 $f_{j,k}$ 最小值即可,复杂度变成了 $\mathcal{O}(n^{2})$。

考虑上面的结论,你会发现我们只需要找打第一个可行的转移点 $k$ 即可。

意味着我们可以维护一个 $g_{i}$ 来表示:前 $i$ 个数,我们最大的一个合法 $k$。这个东西肯定是单调不降的。

转移即:

$$ g_{i}=\max_{pre_{i}-pre{j}\ge pre_{j}-pre_{g_{j}}}\{j\} $$

变形得:

$$ g_{i}=\max_{pre_{i}\ge pre_{j}\times2-pre_{g_{j}}}\{j\} $$

显然判定条件的右边有单调性($pre_{j}+(pre_{j}-pre_{g_{j}})$),左边也有单调性。

然后单调队列优化即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef __int128 _LL;
int n,a[40000010],taskID,f[40000010],que[40000010],head,tail;
LL pre[40000010];
_LL ans;
template<typename T>
void read(T &hhh)
{
    T x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')    f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')    x=(x<<3)+(x<<1)+(c^'0'),c=getchar();
    if(~f)    hhh=x;
    else    hhh=-x;
}
template<typename T>
void write(T x,char las='\n')
{
    static int stk[100],top=0,f=0;
    if(x<0)    x=-x,f=1;
    do stk[++top]=x%10,x/=10; while(x);
    if(f)    putchar('-');
    while(top)    putchar(stk[top--]^'0');
    putchar(las);
}
void generateData(int taskID)
{
    if(taskID)    for(int i=1;i<=n;++i)    read(a[i]);
    else
    {
        static int p[100010],l[100010],r[100010],b[40000010];
        LL x,y,z;
        int m;
        read(x),read(y),read(z),read(b[1]),read(b[2]),read(m);
        const int MOD=(1<<30);
        for(int i=1;i<=m;++i)    read(p[i]),read(l[i]),read(r[i]);
        for(int i=3;i<=n;++i)    b[i]=(LL(x)*b[i-1]%MOD+LL(y)*b[i-2]%MOD+z)%MOD;
        for(int j=1;j<=m;++j)
        {
            for(int i=p[j-1]+1;i<=p[j];++i)    a[i]=(b[i]%(r[j]-l[j]+1))+l[j];
        }
    }
    for(int i=1;i<=n;++i)    pre[i]=pre[i-1]+a[i];
}
int main()
{
    read(n),read(taskID);
    generateData(taskID^1);
    for(int i=1;i<=n;++i)
    {
        while(head<tail&&pre[i]>=(pre[que[head+1]]<<1)-pre[f[que[head+1]]])    ++head;
        f[i]=que[head];
        while(head<tail&&(pre[i]<<1)-pre[f[i]]<=(pre[que[tail]]<<1)-pre[f[que[tail]]])    --tail;
        que[++tail]=i;
    }
    for(int i=n;i;i=f[i])    ans+=_LL(pre[i]-pre[f[i]])*(pre[i]-pre[f[i]]);
    write(ans);
    return 0;
}

Description

Link.

Given is a tree. Every node initially has a color which is different from others'. (called $col_{x}$)

Def: $\text{dis}(x,y)$: the number of different colors on the simple path between x and y.

Supporting the following operations:

  1. RELEASE x: For each $y$ on $\text{path}(x,rt)$, make $col_{y}$=a new color which doesn't exist before.
  2. RECENTER x: Make $x$ become the new root after running RELEASE x.
  3. REQUEST x: Print: for each $y$ in $\text{subtree}(x)$, the sum of $\text{dis}(y,rt)$ divided the number of nodes in $\text{subtree}(x)$.

Solution

Link Cut Tree.

We can know that $\text{dis}(x,rt)$ is the number of Fake Edges on $\text{path}(x,rt)$ pluses one.

If we change a Real Edge $(u,v)$, where $dep_{u}<dep_{v}$ into a Fake Edge, for each node $x$ in $\text{subtree}(v)$, $\text{dis}(x,rt)$ will be decreased by $1$.

Vice versa.

In order to support such operation: decrease the subtree by $1$, we can fix the DFS order of the given tree.

However, we also need to change the root. How can we fix the DFS order of the given tree?

Let's have a discussion. Denote $x$ for the current operating node, $rt$ for the current root.

  1. if $rt=x$: modify the whole tree directly.
  2. if $rt$ isn't in $\text{subtree}(x)$: modify $\text{subtree}(x)$.
  3. if $rt$ is in $\text{subtree}(x)$: modify $\text{subtree}(x)$ and cancel the modfication of $\text{subtree}(rt)$
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
vector<int> e[100010];
int n,m,indfn[100010],outdfn[100010],sjc,fa[100010][20],dep[100010],rtnow=1;
#define check(x,f) ((indfn[x]<indfn[f])|(indfn[x]>outdfn[f])) // check whether x isn't in subtree(f)
void dfs(int x,int las)
{
    dep[x]=dep[las]+1,fa[x][0]=las,indfn[x]=++sjc;
    for(int i=1;i^20;++i)    fa[x][i]=fa[fa[x][i-1]][i-1];
    for(unsigned int i=0;i<e[x].size();++i)    if(e[x][i]^las)    dfs(e[x][i],x);
    outdfn[x]=sjc;
}
int getkth(int x,int k)
{
    if(k==0)    return x;
    else
    {
        for(int i=0;i^20;++i)    if((k>>i)&1)    x=fa[x][i];
        return x;
    }
}
struct LinearTree
{
    struct node
    {
        LL val,tag;
    }nodes[400010];
    void turn(int x,int l,int r)
    {
        if(nodes[x].tag)
        {
            int mid=(l+r)>>1;
            nodes[x<<1].val+=(mid-l+1)*nodes[x].tag;
            nodes[x<<1|1].val+=(r-mid)*nodes[x].tag;
            nodes[x<<1].tag+=nodes[x].tag;
            nodes[x<<1|1].tag+=nodes[x].tag;
            nodes[x].tag=0;
        }
    }
    void ins(int l,int r,int x,int fr,int ba,int val)
    {
        if(fr>ba||l>ba||r<fr)    return;
        if(l>=fr&&r<=ba)    nodes[x].val+=(r-l+1)*val,nodes[x].tag+=val;
        else
        {
            int mid=(l+r)>>1;
            turn(x,l,r);
            ins(l,mid,x<<1,fr,ba,val);
            ins(mid+1,r,x<<1|1,fr,ba,val);
            nodes[x].val=nodes[x<<1].val+nodes[x<<1|1].val;
        }
    }
    LL find(int l,int r,int x,int fr,int ba)
    {
        if(fr>ba||l>ba||r<fr)    return 0;
        if(l>=fr&&r<=ba)    return nodes[x].val;
        else
        {
            int mid=(l+r)>>1;
            turn(x,l,r);
            return find(l,mid,x<<1,fr,ba)+find(mid+1,r,x<<1|1,fr,ba);
        }
    }
    void modify(int x,LL val)
    {
        if(rtnow==x)    ins(1,n,1,1,n,val);
        else if(check(rtnow,x))    ins(1,n,1,indfn[x],outdfn[x],val);
        else
        {
            int tmp=getkth(rtnow,dep[rtnow]-dep[x]-1);
            ins(1,n,1,1,indfn[tmp]-1,val);
            ins(1,n,1,outdfn[tmp]+1,n,val);
        }
    }
}lrt;
struct LinkCutTree
{
    #define wis(x) (nodes[nodes[x].fa].ch[1]==(x))
    #define isrt(x) ((nodes[nodes[x].fa].ch[0]^(x))&&(nodes[nodes[x].fa].ch[1]^(x)))
    struct node
    {
        int ch[2],fa;
        bool rev;
    }nodes[100010];
    void turn_down(int x)
    {
        if(nodes[x].rev)
        {
            swap(nodes[x].ch[0],nodes[x].ch[1]);
            if(nodes[x].ch[0])    nodes[nodes[x].ch[0]].rev^=1;
            if(nodes[x].ch[1])    nodes[nodes[x].ch[1]].rev^=1;
            nodes[x].rev=0;
        }
    }
    void turn_whole(int x)
    {
        if(!isrt(x))    turn_whole(nodes[x].fa);
        turn_down(x);
    }
    void rotate(int x)
    {
        int f=nodes[x].fa,ff=nodes[f].fa,t=wis(x);
        nodes[x].fa=ff;
        if(!isrt(f))    nodes[ff].ch[wis(f)]=x;
        nodes[f].ch[t]=nodes[x].ch[t^1];
        nodes[nodes[x].ch[t^1]].fa=f;
        nodes[x].ch[t^1]=f;
        nodes[f].fa=x;
    }
    void splay(int x)
    {
        turn_whole(x);
        while(!isrt(x))
        {
            int f=nodes[x].fa;
            if(!isrt(f))    rotate((wis(x)^wis(f))?x:f);
            rotate(x);
        }
    }
    int findleft(int x)
    {
        turn_down(x);
        while(nodes[x].ch[0])    x=nodes[x].ch[0],turn_down(x);
        return x;
    }
    void access(int x)
    {
        for(int y=0;x;y=x,x=nodes[x].fa)
        {
            splay(x);
            if(nodes[x].ch[1])    lrt.modify(findleft(nodes[x].ch[1]),1);
            if(y)    lrt.modify(findleft(y),-1);
            nodes[x].ch[1]=y;
        }
    }
    void makert(int x){access(x),splay(x),nodes[x].rev^=1;}
}lct;
char opt[20];
int opx;
template<typename T>
void read(T &hhh)
{
    T x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')    f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')    x=(x<<3)+(x<<1)+(c^'0'),c=getchar();
    if(~f)    hhh=x;
    else    hhh=-x;
}
int main()
{
    read(n),read(m);
    for(int i=1,x,y;i<n;++i)
    {
        read(x),read(y);
        e[x].emplace_back(y);
        e[y].emplace_back(x);
    }
    dfs(1,0);
    for(int i=1;i<=n;++i)    lrt.ins(1,n,1,indfn[i],indfn[i],dep[i]),lct.nodes[i].fa=fa[i][0];
    while(m--)
    {
        scanf("%s",opt),read(opx);
        if(strcmp(opt,"RELEASE")==0)    lct.access(opx);
        else if(strcmp(opt,"RECENTER")==0)    lct.makert(opx),rtnow=opx;
        else
        {
            if(rtnow==opx)    printf("%.10f\n",double(lrt.find(1,n,1,1,n))/n);
            else if(check(rtnow,opx))    printf("%.10f\n",double(lrt.find(1,n,1,indfn[opx],outdfn[opx]))/(outdfn[opx]-indfn[opx]+1));
            else
            {
                int tmp=getkth(rtnow,dep[rtnow]-dep[opx]-1);
                printf("%.10f\n",double(lrt.find(1,n,1,1,indfn[tmp]-1)+lrt.find(1,n,1,outdfn[tmp]+1,n))/(indfn[tmp]+n-outdfn[tmp]-1));
            }
        }
    }
    return 0;
}

Part. 1 Preface

没什么 preface。

Part. 2 实现

具体来说就是把所有关键点按 $\text{dfn}$ 排序,去重,然后求出相邻结点的 $\text{LCA}$,然后加入关键点,去重;然后把关键点和加入的 $\text{LCA}$ 一起按 $\text{dfn}$ 排序,最后把两两之间的 $\text{LCA}$ 拿出来当爸爸然后把右边当儿子。

草说不清楚自己看代码,比传统维护右链代码不知道短到哪里去了。

struct VirtualTree
{
    vector<int> e[1000010]; // 连出来的虚树
    vector<int> build(vector<int> poi) // poi:关键点
    {
        sort(poi.begin(),poi.end(),cmp);
        poi.erase(unique(poi.begin(),poi.end()),poi.end());
        int len=poi.size();
        for(int i=0;i<len-1;++i)    poi.push_back(LCA(poi[i],poi[i+1]));
        sort(poi.begin(),poi.end(),cmp);
        poi.erase(unique(poi.begin(),poi.end()),poi.end());
        len=poi.size();
        for(int i=1;i<len;++i)    e[LCA(poi[i-1],poi[i])].push_back(poi[i]);
        return poi;
    }
}VRT;

「ABC 193A」Discount

Link.

略。

#include<cstdio>
int main()
{
    int a,b;
    scanf("%d %d",&a,&b);
    printf("%.2f\n",(1.0-(double)b/(double)a)*100.0);
    return 0;
}

「ABC 193B」Play Snuke

Link.

排序。

#include<cstdio>
#include<algorithm>
using namespace std;
struct node
{
    int tim,pay,ps;
}nodes[100010];
bool cmp(node one,node ano)
{
    return one.pay<ano.pay;
}
int ans=-1;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)    scanf("%d %d %d",&nodes[i].tim,&nodes[i].pay,&nodes[i].ps);
    sort(nodes+1,nodes+n+1,cmp);
    for(int i=1;i<=n;++i)
    {
        if(((int)((double)nodes[i].tim-0.5)+1)<nodes[i].ps)
        {
            ans=nodes[i].pay;
            break;
        }
    }
    printf("%d\n",ans);
    return 0;
}

「ABC 193C」Unexpressed

Link.

可以容斥,也可以暴力枚举 std::set 去重。

#include<set>
#include<iostream>
#define int long long
using namespace std;
set<int> app;
signed main()
{
    int n,ians=0;
    cin>>n;
    for(int i=2;i*i<=n;++i)
    {
        for(int j=i*i;j<=n;j*=i)
        {
            if(!app.count(j))
            {
                ++ians;
                app.insert(j);
            }
        }
    }
    cout<<n-ians;
    return 0;
}

「ABC 193D」Poker

Link.

开个答案+单位贡献的结构体,然后模拟。

#include<iostream>
#include<iomanip>
using namespace std;
#define int long long
#define ID(c) ((c)-'0')
int k,num[20];
char s[20],t[20];
struct node
{
    int iths[20],sum,c[20];
    int spow(int fur)
    {
        int res=1;
        for(int i=1;i<=fur;++i)    res*=10;
        return res;
    }
    void getscor(char x[])
    {
        for(int i=1;i<=4;++i)    ++c[ID(x[i])];
        for(int i=1;i<=9;++i)
        {
            iths[i]=i*spow(c[i]);
            sum+=iths[i];
        }
    }
    void Op(int pos,int delta)
    {
        c[pos]+=delta;
        sum-=iths[pos];
        if(~delta)    iths[pos]*=10;
        else    iths[pos]/=10;
        sum+=iths[pos];
    }
}tak,aok;
signed main()
{
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
    cin>>k>>(s+1)>>(t+1);
    for(int i=1;i<=9;++i)    num[i]=k;
    for(int i=1;i<=4;++i)    --num[ID(s[i])],--num[ID(t[i])];
    tak.getscor(s);
    aok.getscor(t);
    int winans=0,allans=0;
    for(int i=1;i<=9;++i)
    {
        for(int j=1;j<=9;++j)
        {
            tak.Op(i,1);
            aok.Op(j,1);
            if(tak.sum>aok.sum)
            {
                if(i^j)    winans+=num[i]*num[j];
                else    winans+=num[i]*(num[j]-1);
            }
            tak.Op(i,-1);
            aok.Op(j,-1);
        }
    }
    for(int i=1;i<=9;++i)
    {
        for(int j=1;j<=9;++j)
        {
            if(i^j)    allans+=num[i]*num[j];
            else    allans+=num[i]*(num[j]-1);
        }
    }
    cout<<fixed<<setprecision(6)<<(double)winans/(double)allans<<endl;
    return 0;
}

「ABC 193E」Oversleeping

Link.

开始想到计算几何去了,打了很久。

实际上因为 $500$ 的范围,你可以直接枚举余数然后 exCRT 合并。

#include<cstdio>
#include<climits>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL INF=LONG_LONG_MAX;
namespace NumbTheo
{
    LL ftimes(LL bas,LL fur,LL mod)
    {
        LL res=0;
        while(fur)
        {
            if(fur&1)    res=(res+bas)%mod;
            bas=(bas+bas)%mod,fur>>=1;
        }
        return res;
    }
    LL exGCD(LL one,LL ano,LL &x,LL &y)
    {
        if(ano==0)    return (x=1,y=0,one);
        else
        {
            LL tmp=exGCD(ano,one%ano,y,x);
            y-=(one/ano)*x;
            return tmp;
        }
    }
    LL exCRT(LL n,LL one[],LL ano[])
    {
        LL x,y,res=one[1],M=ano[1];
        for(int i=2;i<=n;++i)
        {
            LL a=M,b=ano[i],c=(one[i]-res%b+b)%b,tmp=exGCD(a,b,x,y),d=b/tmp;
            if(c%tmp)    return -1;
            x=ftimes(x,c/tmp,d);
            res+=x*M,M*=d,res=(res%M+M)%M;
        }
        return (res%M+M)%M;
    }
};
int t;
LL one[10],ano[10],ans;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        LL X,Y,P,Q;
        scanf("%lld %lld %lld %lld",&X,&Y,&P,&Q);
        ans=INF;
        for(LL i=X;i<X+Y;++i)
        {
            for(int j=P;j<P+Q;++j)
            {
                using namespace NumbTheo;
                one[1]=i,ano[1]=((X+Y)<<1),one[2]=j,ano[2]=P+Q;
                LL tmp=exCRT(2,one,ano);
                if(~tmp)    ans=min(ans,tmp);
            }
        }
        if(ans==INF)    printf("infinity\n");
        else    printf("%lld\n",ans);
    }
    return 0;
}

「ABC 193F」Zebraness

Link.

把 $i+j$ 为奇数的地方反转一下,然后连边求最小割。

#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
#define getID(x,y) (((x)-1)*n+(y))
namespace MaxFlow
{
    queue<int> q;
    const LL INF=1e9;
    int S,T,head[10010],Cur[10010],cntot=1,all,vis[10010];
    LL dep[10010];
    struct Edge
    {
        int to,nxt;
        LL c;
        Edge(int A=0,int B=0,LL C=0){to=A,nxt=B,c=C;}
    }as[500010];
    bool bfs()
    {
        for(int i=1;i<=all;++i)    vis[i]=0,dep[i]=INF;
        q.push(S),dep[S]=0,vis[S]=1;
        while(!q.empty())
        {
            int now=q.front();
            q.pop(),vis[now]=0;
            for(int i=head[now];i;i=as[i].nxt)
            {
                int y=as[i].to;
                LL c=as[i].c;
                if(c&&dep[y]>dep[now]+1)
                {
                    dep[y]=dep[now]+1;
                    if(!vis[y])    q.push(y),vis[y]=1;
                }
            }
        }
        return dep[T]<INF;
    }
    LL dfs(int x,LL lav)
    {
        if(x==T)    return lav;
        LL used=0;
        vis[x]=1;
        for(int &i=Cur[x];i;i=as[i].nxt)
        {
            int y=as[i].to;
            LL c=as[i].c;
            if(c&&dep[y]==dep[x]+1&&!vis[y])
            {
                LL tmp=dfs(y,min(lav-used,c));
                as[i].c-=tmp,as[i^1].c+=tmp,used+=tmp;
                if(lav==used)    break;
            }
        }
        if(used<lav)    dep[x]=INF;
        vis[x]=0;
        return used;
    }
    LL Dinic()
    {
        LL res=0;
        while(bfs())
        {
            for(int i=1;i<=all;++i)    vis[i]=0,Cur[i]=head[i];
            res+=dfs(S,INF);
        }
        return res;
    }
}using namespace MaxFlow;
int n;
int rop()
{
    char res=getchar();
    while((res^'B')&&(res^'W')&&(res^'?'))    res=getchar();
    if(res=='?')    return -1;
    else if(res=='B')    return 1;
    else    return 0;
}
void addEdge(int one,int ano,int lav)
{
    as[++cntot]=Edge(ano,head[one],lav);
    head[one]=cntot;
    as[++cntot]=Edge(one,head[ano],0);
    head[ano]=cntot;
}
int main()
{
    scanf("%d",&n),all=n*n;
    S=(++all),T=(++all);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)
        {
            int x=rop();
            if(~x)
            {
                if((i+j)&1)    x^=1;
                if(x)    addEdge(S,getID(i,j),INF);
                else    addEdge(getID(i,j),T,INF);
            }
        }
    }
    for(int i=1;i<n;++i)
    {
        for(int j=1;j<=n;++j)    addEdge(getID(i,j),getID(i+1,j),1),addEdge(getID(i+1,j),getID(i,j),1);
    }
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<n;++j)    addEdge(getID(i,j),getID(i,j+1),1),addEdge(getID(i,j+1),getID(i,j),1);
    }
    printf("%lld\n",2*n*(n-1)-Dinic());
    return 0;
}