#网络流,树状数组#JZOJ 4020 Revolution with JZOJ 4018 Magic

CF297E Mystic Carvings=JZOJ 4018 Magic


JZOJ 4020 Revolution

题目

有一个\(n*m(n,m\leq 20)\)的网格图
这格子有收益当且仅当上下左右都被投资或者自身被投资,
问最多能赚多少


分析

一看就是最小割,但是实际建图比较难搞,因为还有相邻这种建图
那可以奇偶性建图,收益\((i,j)\)连源点\((i+j=even)\),收益\((x,y)\)连汇点\(x+y=odd\)
投资\((i',j')\)\((i,j)\),投资\((x',y')\)\((x,y)\),相邻因为两种点都有可能,所以要交错连接一条无限大的边


代码

#include <cstdio>
#include <cctype>
#include <queue>
#define rr register
#define ide(i,j) ((i-1)*m+j)
using namespace std;
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
const int inf=707406378; struct node{int y,w,next;}e[10011];
int ls[821],dis[821],cost[21][21],reward[21][21],n,m,k=1,s,t,ans;
inline void add(int x,int y,int w){
	e[++k]=(node){y,w,ls[x]}; ls[x]=k;
	e[++k]=(node){x,0,ls[y]}; ls[y]=k;
}
inline signed bfs(int s){
    for (rr int i=1;i<=t;++i) dis[i]=0;
    rr queue<int>q; q.push(s); dis[s]=1;
    while (q.size()){
        rr int x=q.front(); q.pop();
        for (rr int i=ls[x];i;i=e[i].next)
        if (e[i].w>0&&!dis[e[i].y]){
            dis[e[i].y]=dis[x]+1;
            if (e[i].y==t) return 1;
            q.push(e[i].y);
        }
    }
    return 0;
}
inline signed dfs(int x,int now){
    if (x==t||!now) return now;
    rr int rest=0,f;
    for (rr int i=ls[x];i;i=e[i].next)
    if (e[i].w>0&&dis[e[i].y]==dis[x]+1){
        rest+=(f=dfs(e[i].y,min(now-rest,e[i].w)));
        e[i].w-=f; e[i^1].w+=f;
        if (now==rest) return rest;
    }
    if (!rest) dis[x]=0;
    return rest;
}
signed main(){
    scanf("%d%d",&n,&m),s=n*m*2+1,t=s+1;
    for (rr int i=1;i<=n;++i)
    for (rr int j=1;j<=m;++j){
        rr char c=getchar();
        while (!isalnum(c)) c=getchar();
        if (isdigit(c)) cost[i][j]=c^48;
        else if (islower(c)) cost[i][j]=c-87;
            else cost[i][j]=c-29;
    }
    for (rr int i=1;i<=n;++i)
    for (rr int j=1;j<=m;++j){
        rr char c=getchar();
        while (!isalnum(c)) c=getchar();
        if (isdigit(c)) reward[i][j]=c^48;
        else if (islower(c)) reward[i][j]=c-87;
            else reward[i][j]=c-29;
        ans+=reward[i][j];
    }
    for (rr int i=1;i<=n;++i)
    for (rr int j=1;j<=m;++j)
    if ((i^j)&1){
        add(s,ide(i,j)+n*m,cost[i][j]);
        add(ide(i,j)+n*m,ide(i,j),reward[i][j]);
        for (rr int k=0;k<4;++k){
            rr int x=i+dx[k],y=j+dy[k];
            if (x<1||x>n||y<1||y>m) continue;
            add(ide(i,j),ide(x,y)+n*m,inf);
            add(ide(i,j)+n*m,ide(x,y),inf);
        }
    }else add(ide(i,j),ide(i,j)+n*m,reward[i][j]),
            add(ide(i,j)+n*m,t,cost[i][j]);
    while (bfs(s)) ans-=dfs(s,inf);
    return !printf("%d",ans);   
}

JZOJ 4018 Magic

题目

有一个圆,圆上有\(2*n\)个点按顺序编号,圆内有\(n\)条弦,这些弦不相交于圆上,
选取三根弦,这六个点顺次连接变成一个正六边形的形状,把三条弦重新连接,
如果这个新的六边形关于正六边形外接圆的圆心中心对称,那么即为一种方案,问共有多少种方案


分析


分类讨论,
首先答案就是前两种的总和,显然不好处理
那就用总方案\(C(n,3)\)减去后三种,用树状数组求出当前边的左边的边个数\(x\)
就可以求出与该边相交的边个数\(z\),用\(n-1\)减去这两种边就是当前边右边的边\(y\)
首先第三种情况就是\(x*y\),第四五种情况都是\((x+y)*z\),因为会算重,所以要除以2


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int n,f[200011],c[200011]; long long ans,ans1;
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline void update(int x){for (;x<=n;x+=-x&x) ++c[x];}
inline signed query(int x){rr int ans=0; for (;x;x-=-x&x) ans+=c[x]; return ans;}
signed main(){
    n=iut();
    for (rr int i=1,x;i<=n;++i)
        x=iut(),f[f[x]=iut()]=x;
    ans=1ll*n*(n-1)*(n-2)/6,n<<=1;
    for (rr int i=1,cnt=0;i<=n;++i)
    if (i>f[i]){
        rr int x=cnt-query(f[i]-1),z=i-f[i]-1-x*2,y=n/2-1-x-z;
        update(f[i]),++cnt,ans-=1ll*x*y,ans1+=1ll*(x+y)*z;
    }
    return !printf("%lld",ans-ans1/2);
}
posted @ 2020-02-09 20:59  lemondinosaur  阅读(164)  评论(0编辑  收藏  举报