【题解】CQOI2017老C的方块

  网络流真的是一种神奇的算法。在一张图上面求感觉高度自动化的方案一般而言好像都是网络流的主阵地。讲真一开始看到这道题也有点懵,题面很长,感觉很难的样子。不过,仔细阅读了题意之后明白了:我们所要做的就是要用最小的代价,使得最后的图中不能出现给定的四种图案。

  实际上之前做过一道非常毒瘤的网络流题目【无限之环】。当时就思考了一下:为什么出题人规定不能旋转直线管子?原因就是因为这样的图建不出来,它与不具有其他的管子的特点。那么可以断定:给出的这四个图案必然也是非常特殊的图形,否则不会只有这四个/不会是这四个。于是观察这四个图案,不难发现它们的特征:以一对中间夹了特殊边的方块为中心,分别有另一方块随意连接在上方。

  我们从这个中心开始入手:由于要求最小值,所以-->最小割 / 费用流。但一个直观的感觉,它是在很多的方案当中做出选择,与最小割是比较贴合的。(每一种图案牵扯到四个方块,拿掉任何一个就可以破坏这个图形)如果每一种方案彼此平行,只需建出图暴力跑即可。可是会有交叉:当我们删去一个方块时,可能同时破坏了两个方案。

  观察图案,由于上下,左右交错,一个中心只可能出现在一个方案中,但其两侧的方块却可能出现在不同的方案当中。于是我们对于这样两侧的方块进行黑白染色,保证每一种方案当中所牵涉到的另两个方块分别属于不同的颜色(由于题目的特殊性质是可以做到的)。

  那么建边的方式也十分的自然了:

  源点-->所有白色的点,边权为代价;所有白色的点-->中心连边,边权为INF;所有的中心 = 两条边 = 边权为各个方块的代价。所有的中心 --> 黑点,黑点 --> 汇点,边权为代价。此时的最小割所代表的意义即为我们做出的最小代价方案选择。

  做出这题还是比较开心的,代码跑得也很快。不过细节比较多,要注意一下……

// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;
#define maxn 300000
#define maxm 800000
#define ll long long
#define INF 9999999
int C, R, n, S, T, tot;
int Color[maxn], cnt;
int cnp = 2, head[maxn], cur[maxn];
int lev[maxn];
map <int, int> Map;
int dx[10] = {1, 1, 1, 1, 0, 0, -1, -1, -1, -1};
int dy[10] = {-1, 0, 1, 2, -1, 2, -1, 0, 1, 2};

struct edge
{
    int to, last, f;
}E[maxm];

struct node
{
    int x, y, w;
}P[maxn];

bool cmp(node a, node b)
{
    if(a.x != b.x) return a.x < b.x;
    return a.y < b.y;
}

int read()
{
    int x = 0, k = 1;
    char c;
    c = getchar();
    while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) k = -1; c = getchar(); }
    while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar();
    return x * k;
}

void add(int u, int v, int f)
{
    E[cnp].to = v, E[cnp].f = f, E[cnp].last = head[u], head[u] = cnp ++;
    E[cnp].to = u, E[cnp].f = 0, E[cnp].last = head[v], head[v] = cnp ++;
}

bool Bfs()
{
    queue <int> q;
    memset(lev, 0, sizeof(lev));
    q.push(S); lev[S] = 1;
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        for(int i = head[u]; i; i = E[i].last)
        {
            int v = E[i].to;
            if(!lev[v] && E[i].f)
            {
                lev[v] = lev[u] + 1;
                q.push(v);
            }
        }
        if(lev[T]) return 1;
    }
    return 0;
}

int Dfs(int u, int nf)
{
    if(u == T) return nf;
    int lf = 0;
    for(int i = cur[u]; i; i = E[i].last)
    {
        int v = E[i].to;
        if(lev[v] == lev[u] + 1 && E[i].f)
        {
            int af = Dfs(v, min(E[i].f, nf));
            nf -= af, lf += af;
            E[i].f -= af, E[i ^ 1].f += af;
            if(!nf) return lf;
            cur[u] = i;
        }
    }
    if(!lf) lev[u] = -1;
    return lf;
}

int Dinic()
{
    int ans = 0;
    while(Bfs())
    {
        memcpy(cur, head, sizeof(head));
        ans += Dfs(S, INF);
    }
    return ans;
}

int id(int x, int y)
{
    ll k = 1ll * R * (x - 1) + 1ll * y;
    if(Map[k]) return Map[k];
    else return Map[k] = ++ tot;
}

int Check(int x, int y)
{
    ll k = 1ll * R * (x - 1) + 1ll * y;
    if(Map[k]) return Map[k];
    else return 0;
}

void Get_Graph(int p)
{
    int a = ++ tot, b = ++ tot, c = ++ tot;
    add(a, b, P[p].w); add(b, c, P[p + 1].w);
    for(int i = 0; i < 10; i ++)
    {
        int xx = P[p].x + dx[i], yy = P[p].y + dy[i], tem;
        if(xx < 1 || xx > C || yy < 1 || yy > R) continue;
        if(tem = Check(xx, yy))
        {
            if(Color[tem] == 2) add(tem, a, INF);
            else add(c, tem, INF);
        }
    }
}

int main()
{
    C = read(), R = read();
    swap(C, R); n = read();
    S = 0, T = 2 * n + 100;
    for(int i = 1; i <= n; i ++)
    {
        int x = read(), y = read(), w = read();
        swap(x, y);
        if((x & 1) && ((!((y - 1) % 4)) || (!((y - 2) % 4))))
        {
            P[++ cnt].x = x, P[cnt].y = y;
            P[cnt].w = w; continue;
        }
        if((!(x & 1)) && ((!((y - 3) % 4)) || (!((y - 4) % 4))))
        {
            P[++ cnt].x = x, P[cnt].y = y;
            P[cnt].w = w; continue;
        }
        if(x & 1)
        {
            if(((y - 3) % 4)) add(S, id(x, y), w), Color[id(x, y)] = 2;
            else add(id(x, y), T, w), Color[id(x, y)] = 1;
        }
        else
        {
            if(!((y - 2) % 4)) add(id(x, y), T, w), Color[id(x, y)] = 1;
            else add(S, id(x, y), w), Color[id(x, y)] = 2;
        }
    }
    sort(P + 1, P + 1 + cnt, cmp);
    for(int i = 1; i <= cnt; i += 1)
    {
        if(P[i + 1].x != P[i].x) continue;
        if(P[i + 1].y != P[i].y + 1) continue;
        Get_Graph(i);
    }
    printf("%d\n", Dinic());
    return 0;
}

原文地址:https://www.cnblogs.com/twilight-sx/p/9189329.html

时间: 2024-11-08 19:01:30

【题解】CQOI2017老C的方块的相关文章

bzoj4823: [Cqoi2017]老C的方块(最小割)

4823: [Cqoi2017]老C的方块 题目:传送门 题解: 毒瘤题ORZ.... 太菜了看出来是最小割啥边都不会建...狂%大佬强强强   黑白染色?不!是四个色一起染,四层图跑最小割... 很惊奇的发现染完色之后只要是不喜欢的图形都一定可以由黄-->黑-->红-->绿 组成 那就很nice啦...兴高采烈的去敲代码...结果10^5*10^5???搞毛线...太弱了ORZ,又看了一波大佬的操作,用map存! woc...不谈了不谈了...撸撸撸(分情况分到想屎...虽然不多) 注

[bzoj4823][洛谷P3756][Cqoi2017]老C的方块

Description 老 C 是个程序员. 作为一个懒惰的程序员,老 C 经常在电脑上玩方块游戏消磨时间.游戏被限定在一个由小方格排成的R行C列网格上 ,如果两个小方格有公共的边,就称它们是相邻的,而且有些相邻的小方格之间的公共边比较特殊.特殊的公共边排 列得有很强的规律.首先规定,第1行的前两个小方格之间的边是特殊边.然后,特殊边在水平方向上每4个小方格为 一个周期,在竖直方向上每2个小方格为一个周期.所有的奇数列与下一列之间都有特殊边,且所在行的编号从左到 右奇偶交替.下图所示是一个R =

BZOJ 4823: [Cqoi2017]老C的方块

分析: 我觉得我的网络流白学了...QAQ... 其实数据范围本是无法用网络流跑过去的,然而出题者想让他跑过去,也就跑过去了... 看到题目其实感觉很麻烦,不知道从哪里入手,那么仔细观察所给出的有用信息... 我们考虑网格图是一个含有挡板的图,这个挡板的分布很有规律,大概是每一行的相邻两个挡板都隔了四个格子,并且奇数行的排列相同,偶数行的排列相同... 然后考虑不合法的方块形状有什么共同点:仔细观察就会发现,所有的不合法图形中,挡板的左边至少有一个格子,右边至少有一个格子,并且左边的格子连着一个

Bzoj4823 [Cqoi2017]老C的方块

没有题面,懒得手打 网络流 最小割 码农题(误) 一开始是冲着n<=5000的部分分写了网络流,结果神奇地发现似乎就是正解. 说好的dinic时间复杂度上界$O(V^2 E)$呢……网络流不愧是玄学算法. 放一张题目里的图 四种图案: 观察这四种图案和它们旋转/翻转以后的样子,可以发现一个共同点:每种图案都是由“中心一条蓝色边和它相邻的两个方块”,以及另外两个邻接的方块组成的 范围画出来就是这个样子: 可以发现“另外两个邻接的方块”肯定一个在蓝线左边一个在蓝线右边. 先说一种错误的想法: 将每个

[CQOI2017]老C的键盘

[CQOI2017]老C的键盘 题目描述 额,网上题解好像都是用的一大堆组合数,然而我懒得推公式. 设\(f[i][j]\)表示以\(i\)为根,且\(i\)的权值为\(j\)的方案数. 转移: \[ f[i][j]=\sum f[sn_1][k]*f[sn_2][q] \] 需要判断一下\(k,q\)与\(j\)的关系满不满足题意就行了. 但是这样的答案显然不对,因为有些权值可能多次出现. 换句话说,有些权值可能没有出现.所以我们就用那个经典的容斥,枚举颜色数上界. 设\(g[s]\)表示颜色

bzoj4822: [Cqoi2017]老C的任务

4822: [Cqoi2017]老C的任务 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 284  Solved: 152[Submit][Status][Discuss] Description 老 C 是个程序员. 最近老 C 从老板那里接到了一个任务--给城市中的手机基站写个管理系统.作为经验丰富的程序员,老 C 轻松 地完成了系统的大部分功能,并把其中一个功能交给你来实现.由于一个基站的面积相对于整个城市面积来说非常 的小,因此每个的基站

[BZOJ4824][CQOI2017]老C的键盘(树形DP)

4824: [Cqoi2017]老C的键盘 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 193  Solved: 149[Submit][Status][Discuss] Description 老 C 是个程序员. 作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序 在某种神奇力量的驱使之下跑得非常快.小 Q 也是一个程序员.有一天他悄悄潜入了老 C 的家中,想要看看这个 键盘究竟有何妙

@loj - [email&#160;protected] 「CQOI2017」老 C 的方块

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 老 C 是个程序员. 作为一个懒惰的程序员,老 C 经常在电脑上玩方块游戏消磨时间.游戏被限定在一个由小方格排成的 R 行 C 列网格上,如果两个小方格有公共的边,就称它们是相邻的,而且有些相邻的小方格之间的公共边比较特殊. 特殊的公共边排列得有很强的规律.首先规定,第 1 行的前两个

[bzoj4824][洛谷P3757][Cqoi2017]老C的键盘

Description 老 C 是个程序员. 作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序 在某种神奇力量的驱使之下跑得非常快.小 Q 也是一个程序员.有一天他悄悄潜入了老 C 的家中,想要看看这个 键盘究竟有何妙处.他发现,这个键盘共有n个按键,这n个按键虽然整齐的排成一列,但是每个键的高度却互不相同 .聪明的小 Q 马上将每个键的高度用 1 ~ n 的整数表示了出来,得到一个 1 ~ n 的排列 h1, h2,..., hn .为