「清华集训 2017」无限之环

无限之WA

https://www.luogu.org/problemnew/show/P4003

本题如果知道是网络流的话,其实建图不算特别神奇,但是比较麻烦。

数据范围过大,插头dp不能处理,而且是一个网格图,考虑网络流。

先看是不是二分图?

每个格子只会和相邻四个格子发生关系

所以,黑白染色正好。

i+j为偶数左部点,i+j为奇数右部点

不漏水是什么?

每个管道的四个口都能和别的接好。

S向左,右向T连口数的容量,费用为0的边

考虑怎么能把左右连在一起。

考虑要上下左右四个方向匹配,那么必然还要拆点(但是这些点之间是并列关系)。

每个本点向四个分点之间考虑连边(在这里考虑旋转以及费用)

考虑通过流量流过来表示选择旋转与否,恰当的边赋恰当的值。

sz=1:

自己方向是(1,0)相邻方向(1,1),对面(1,2)

sz=2:

直线型:自己方向(1,0),另外两个不连(因为不能转),

非直线型:自己两个方向(1,0),每个自己方向+2(-2),连(1,1),(手动模拟一下这样选择的四种流法对应四种旋转的位置)

sz=3:

自己(1,0),剩下一个0位置,距离为1的向它连(1,1),距离为2的向它连(1,2)

sz=4

四个方向(1,0);

(注意,右边的单向边方向和左边的完全相反)

然后左右分点之间相邻的关系上下,左右,下上,右左,左分点连到对应的右分点。

然后跑最小费用最大流。

至于-1

先判断黑格口数是不是等于白格口数

然后如果最大流不是口数(满流)的话,就-1

(其实数据没有-1的点23333~~~)

这样,一条流的意义是什么?左边的某个口和右边的某个口,通过旋转或者不旋转连接在了一起,同时两个管道的需求都少了1

由于所有边的流量都是1(除了和ST连的),所以每个口只会流出1流,减少1的需求,

如果最后满流

那么意味着,所有的口都满足了自己的需求,即每个口都连上了。而且每个流都合法。

出错点:

1.数组开小了。。。。has[4],四位二进制数,少开一位。。。(这个导致开O2之后超级厌氧,全部输出-1WA掉,一定程度上转移了查错重心。。。)

2.提取四位二进制数的时候,习惯性地写成了while(tmp) has[++tot]=tmp%2,tmp>>=1;然鹅,最高位是0的话,没有提取完4位就break了。而且has没有memset,高位就存上了之前可能的1.。。。。导致WA死。。

其实开始找规律一点没错。。。。但是由于while高位0的锅,以为找错了,,,最后还打了暴力判断。。。。

代码:(得开O2)

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^‘0‘)
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;bool fl=false;
    while(!isdigit(ch=getchar()))(ch==‘-‘)&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=2000+2;
const int inf=0x3f3f3f3f;
int n,m,s,t;
struct node{
    int nxt,to;
    int w,v;
}e[(5*N+4*N+N*6)*2];
int hd[5*N],cnt=1;
void add(int x,int y,int w,int v){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    e[cnt].w=w;
    e[cnt].v=v;
    hd[x]=cnt;

    e[++cnt].nxt=hd[y];
    e[cnt].to=x;
    e[cnt].w=0;
    e[cnt].v=-v;
    hd[y]=cnt;
}
int incf[5*N],dis[5*N];
int pre[5*N];
bool vis[5*N];
queue<int>q;
bool spfa(){
    while(!q.empty()) q.pop();
    memset(vis,0,sizeof vis);
    memset(dis,inf,sizeof dis);
    dis[s]=0;
    incf[s]=inf;
    pre[s]=0;
    q.push(s);
    while(!q.empty()){
        int x=q.front();q.pop();
        vis[x]=0;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(e[i].w&&dis[y]>dis[x]+e[i].v){
                dis[y]=dis[x]+e[i].v;
                pre[y]=i;
                incf[y]=min(e[i].w,incf[x]);
                if(!vis[y]){
                    vis[y]=1;
                    q.push(y);
                }
            }
        }
    }
    if(dis[t]==inf) return false;
    return true;
}
int ans,maxflow;
void upda(){
    int x=t;
    while(pre[x]){
        e[pre[x]].w-=incf[t];
        e[pre[x]^1].w+=incf[t];
        x=e[pre[x]^1].to;
    }
    ans+=incf[t]*dis[t];
    maxflow+=incf[t];
    //cout<<" ans "<<ans<<" "<<maxflow<<endl;
}
int has[10],tot;
int mp[N][N];
int sz[N];
int num(int x,int y,int k){//5 is itself
    return ((x-1)*m+y-1)*5+k;
}
int main(){
    rd(n);rd(m);
    s=0,t=n*m*5+1;
    for(reg i=1;i<=16;++i){
        sz[i]=sz[i>>1]+(i&1);
    }
    int le=0,ri=0;
    for(reg i=1;i<=n;++i){
        for(reg j=1;j<=m;++j){
            rd(mp[i][j]);
            if((i+j)%2==0) add(s,num(i,j,5),sz[mp[i][j]],0),le+=sz[mp[i][j]];
            else add(num(i,j,5),t,sz[mp[i][j]],0),ri+=sz[mp[i][j]];
        }
    }
    if(le!=ri){
        printf("-1");return 0;
    }
    for(reg i=1;i<=n;++i){
        for(reg j=1;j<=m;++j){
            for(reg l=1;l<=4;++l){
                has[l]=(mp[i][j]>>(l-1))&1;
            }
            int now=num(i,j,5);
            tot=sz[mp[i][j]];
            if((i+j)%2==0){
                switch(tot){
                    case 0:{
                        break;
                    }
                    case 1:{
                        int pos=0;
                        //cout<<" find "<<mp[i][j]<<" :: "<<has[1]<<" "<<has[2]<<" "<<has[3]<<" "<<has[4]<<endl;
                        for(reg l=1;l<=4;++l) if(has[l]) {
                            pos=l;
                        }
                        add(now,num(i,j,pos),1,0);
                        add(now,num(i,j,pos%4+1),1,1);
                        add(now,num(i,j,pos==1?4:pos-1),1,1);
                        add(now,num(i,j,(pos+2<=4)?pos+2:pos-2),1,2);
                        break;
                    }
                    case 2:{
                        if(mp[i][j]==5||mp[i][j]==10){
//    cout<<" dkfjdf "<<endl;
                            for(reg l=1;l<=4;++l){
                                if(has[l]) add(now,num(i,j,l),1,0);
                            }
                        }else{
                            for(reg l=1;l<=4;++l){
                                if(has[l]) {
                                    add(now,num(i,j,l),1,0);
                                    add(num(i,j,l),num(i,j,(l+2<=4)?l+2:l-2),1,1);
                                }
                            }
                        }
                        break;
                    }
                    case 3:{
                        for(reg l=1;l<=4;++l){
                            if(has[l]){
                                add(now,num(i,j,l),1,0);
                            }else{
                                add(num(i,j,(l+1)<=4?l+1:l-3),num(i,j,l),1,1);
                                add(num(i,j,(l+3)<=4?l+3:l-1),num(i,j,l),1,1);
                                add(num(i,j,(l+2)<=4?l+2:l-2),num(i,j,l),1,2);
                            }
                        }
                        break;
                    }
                    case 4:{
                        for(reg l=1;l<=4;++l){
                            add(now,num(i,j,l),1,0);
                        }
                        break;
                    }
                }
            }else{
                switch(tot){
                    case 0:{
                        break;
                    }
                    case 1:{
                        int pos=0;
                        for(reg l=1;l<=4;++l) if(has[l]){
                             pos=l;
                        }
                        add(num(i,j,pos),now,1,0);
                        add(num(i,j,pos%4+1),now,1,1);
                        add(num(i,j,pos==1?4:pos-1),now,1,1);
                        add(num(i,j,(pos+2<=4)?pos+2:pos-2),now,1,2);
                        break;
                    }
                    case 2:{
                        if(mp[i][j]==5||mp[i][j]==10){
                            for(reg l=1;l<=4;++l){
                                if(has[l]) add(num(i,j,l),now,1,0);
                            }
                        }else{
                            for(reg l=1;l<=4;++l){
                                if(has[l]) {
                                    add(num(i,j,l),now,1,0);
                                    add(num(i,j,(l+2<=4)?l+2:l-2),num(i,j,l),1,1);
                                }
                            }
                        }
                        break;
                    }
                    case 3:{
                        for(reg l=1;l<=4;++l){
                            if(has[l]){
                                add(num(i,j,l),now,1,0);
                            }else{
                                add(num(i,j,l),num(i,j,(l+1)<=4?l+1:l-3),1,1);
                                add(num(i,j,l),num(i,j,(l+3)<=4?l+3:l-1),1,1);
                                add(num(i,j,l),num(i,j,(l+2)<=4?l+2:l-2),1,2);
                            }
                        }
                        break;
                    }
                    case 4:{
                        for(reg l=1;l<=4;++l){
                            add(num(i,j,l),now,1,0);
                        }
                        break;
                    }
                }
            }
            if((i+j)%2==0){
                if(i>1) add(num(i,j,1),num(i-1,j,3),1,0);
                if(i<n)    add(num(i,j,3),num(i+1,j,1),1,0);
                if(j>1) add(num(i,j,4),num(i,j-1,2),1,0);
                if(j<m) add(num(i,j,2),num(i,j+1,4),1,0);
            }
        }
    }
    while(spfa()) upda();
    //cout<<" maxflow "<<maxflow<<endl;
    if(maxflow!=le){
        puts("-1");return 0;
    }
    printf("%d",ans);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/12/14 21:08:15
*/

总结:

这个题的突破口的话,

发现插头dp不行,那么网格图,可能就是网络流(数据范围也支持)

黑白染色可以。那么考虑最终合法的结果是什么意义。然后处理好旋转连边。

(发现没有,直线型为什么不能转?因为这样会同时转2个点!网络流没办法处理这种旋转(除非你大力讨论))

原文地址:https://www.cnblogs.com/Miracevin/p/10122508.html

时间: 2024-10-08 15:00:18

「清华集训 2017」无限之环的相关文章

[LOJ#2325]「清华集训 2017」小Y和恐怖的奴隶主

[LOJ#2325]「清华集训 2017」小Y和恐怖的奴隶主 试题描述 "A fight? Count me in!" 要打架了,算我一个. "Everyone, get in here!" 所有人,都过来! 小Y是一个喜欢玩游戏的OIer.一天,她正在玩一款游戏,要打一个Boss. 虽然这个Boss有 \(10^{100}\) 点生命值,但它只带了一个随从--一个只有 \(m\) 点生命值的"恐怖的奴隶主". 这个"恐怖的奴隶主&qu

[LOJ#2328]「清华集训 2017」避难所

[LOJ#2328]「清华集训 2017」避难所 试题描述 "B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?" "大概是因为出了一些事故吧,否则这道题就不叫避难所了." "唔,那你之后会去哪呢?" "去一个没有冬天的地方." 对于一个正整数 \(n\),我们定义他在 \(b\) 进制下,各个位上的数的乘积为 \(p = F(n, b)\). 比如 \(F(3338, 10) = 216\). 考虑这样一个问题,已知 \

[LOJ#2327]「清华集训 2017」福若格斯

[LOJ#2327]「清华集训 2017」福若格斯 试题描述 小d是4xx9小游戏高手. 有一天,小d发现了一个很经典的小游戏:跳青蛙. 游戏在一个 \(5\) 个格子的棋盘上进行.在游戏的一开始,最左边的两个格子上各有一个向右的青蛙,最右边的两个格子上各有一个向左的青蛙. 每次移动可以选取一个青蛙,向这只青蛙的前方移动一格到空格子中或跳过前方的一个不同朝向的青蛙并移动到空格子中. 为了使你更好地理解这个游戏,我们下发了一个游戏demo作为参考(注意:这个demo中的棋盘大小和题目中并不相同).

[LOJ#2331]「清华集训 2017」某位歌姬的故事

[LOJ#2331]「清华集训 2017」某位歌姬的故事 试题描述 IA是一名会唱歌的女孩子. IOI2018就要来了,IA决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符,第iii个音符的音高为 \(h_i\).IA的音域是 \(A\),她只能唱出 \(1\sim A\) 中的正整数音高.因此 \(1\le h_i\le A\). 在写歌之前,IA需要确定下这首歌的结构,于是她写下了 \(Q\) 条限制,其中第 \(i\) 条为:编号在 \(l_i\) 到 \(r_

LibreOJ #2325. 「清华集训 2017」小Y和恐怖的奴隶主(矩阵快速幂优化DP)

哇这题剧毒,卡了好久常数才过T_T 设$f(i,s)$为到第$i$轮攻击,怪物状态为$s$时对boss的期望伤害,$sum$为状态$s$所表示的怪物个数,得到朴素的DP方程$f(i,s)=\sum \frac{1}{sum+1}*(f(i+1,s')+[s==s'])$ 状态数只有$C_{8+3}^3=165$个,所以就可以矩乘优化啦.再加上一个用于转移的$1$,矩阵大小是$166*166$的,因为多组询问,所以可以先把$2$的所有次幂的矩阵都预处理出来. 然后会发现复杂度是$O(T*166^3

loj2322 「清华集训 2017」Hello world!

https://loj.ac/problem/2322 先吐槽一下,sb数据毁我青春败我前程. 首先,一个数开根开不了多少次. 当我们把它开到1的时候,我们以后就不需要开他了,我们可以利用并查集跳过他,这是套路. 但是这个每次走$k$步,让人很头痛. 于是乎……分块 首先,对于$k$比较大的情况,我们可以暴力跳.否则, 我们对于每个$k$建一棵树,第$i$棵树上$x$的父亲是原树上$x$的第$i$个祖先 然后树剖,用线段树或者树状数组维护一下就好了. 然后!!打开榜,rk1,啥子,并查集+暴力跳

「清华集训 2017」某位歌姬的故事

题目链接 问题分析 吐槽一下这个预处理比DP还长的题-- 首先对限制从小到大排序,然后不难发现对于每一种大小限制都是独立的.离散后考虑\(F[i][j]\)表示以\(i\)结尾,上一个音高为限制大小的位置\(j\)的方案种数.不难发现对于一些右端点相同的限制,左端点最右的限制才有效.这样就可以\(n^2\)动规了. 由于要离散化,所以细节很多. 参考程序 程序没有显式的离散化,并且大量使用结构体,所以又慢又长. #include <bits/stdc++.h> #define LL long

Loj #6703 -「清华集训 2017」生成树计数

\[ \sum _{\sum v_i = n-2} \prod (a_i ^{v_i+1} * (v_i+1) ^ m /v_i!) *(\sum (v_i+1)^m) \] 将 \(\sum (v_i+1)^m\) 中的贡献分开算. 我们有两个生成函数. 第一个: \[ \sum _i a^{i+1} * (v_i+1)^m x^i / i! \] 第二个: \[ \sum _i a^{i+1} * (i+1)^{2m} x^i / i! \] 先将 \(\prod a\) 算到前面. 令:

@loj - [email&#160;protected] 「清华集训 2017」生成树计数

目录 @[email protected] @[email protected] @正文@ @补充@ @accepted [email protected] @[email protected] @[email protected] 在一个 s 个点的图中,存在 s - n 条边,使图中形成了 n 个连通块,第 i 个连通块中有 \(a_i\) 个点. 现在我们需要再连接 n - 1 条边,使该图变成一棵树.对一种连边方案,设原图中第 i 个连通块连出了 \(d_i\) 条边,那么这棵树 T 的