题意

给出一个 \(n\times m\) 的棋盘,上面有一些格子是不能经过的。求有多少种欧拉回路可以经过所有可经过到格子。\(n,m\le 12\) 。

分析

上个月就看了一下插头dp,然而这道题写不出来。现在来看其实也非常好写,只要把情况讨论清楚,对插头dp理解好就可以了。

我们要求的是欧拉回路,只能有一个环,所以要记录连通性状态,而不仅仅是像 hdu1693 这题一样,只记录轮廓线上是否有插头。

用 \(f(i,j,S)\) 表示转移到 \((i,j)\) 这个格子,轮廓线上方到格子全部被覆盖到,轮廓线状态为 \(S\) 的方案数,考虑如何表示状态 \(S\) 。

观察轮廓线上方的连通性,可以发现,任意两条线都是不相交的,所以插头的匹配情况可以用一个合法的括号序列来表示。因此我们使用三进制表示法,0='',1='(',2=')' 。接下来讨论各种情况。

下面的内容自己画一画图,讨论一下就可以得出了。

将轮廓线从 1 到 \(m+1\) 标号,那么若当前为 \((i,j)\) ,那么此处的下插头标号为 \(j\) ,这个值设为 \(p\) ,右插头标号为 \(j+1\) ,这个值设为 \(q\) 。\((i,j-1)\) 的左插头标号为 \(j\) ,这个值设为 \(x\),上插头标号为 \(j+1\) ,值设为 \(y\) 。

  • 此处是洞,那么只从 \(x=y=0\) 转移到 \(p=q=0\) 。否则:

  • \(x=y=0\) ,转移到 \(p=1,q=2\) ,新建一个连通分量

  • \(x=y=1\) ,转移到 \(p=q=0\) ,\(y\) 对应的右括号改为左括号

  • \(x=y=2\) ,转移到 \(p=q=0\) ,\(x\) 对应的左括号改为右括号

  • \(x,y\) 中只有一个为 0,转移到 \(p=x+y,q=0\) 或 \(p=0,q=x+y\) ,延续之前到路线或者在此处转弯

  • \(x=1,y=2\) ,这是对当前的整个连通分量的末端合并,由于我们只能有一条回路,所以这个转移只能在棋盘的最后一个可走位置转移,\(p=q=0\) 。

  • \(x=2,y=1\) ,合并两个连通分量,\(p=q=0\) 。

讨论就这样啦!

写法上来说,用数组 \(f,g\) 滚动进行dp,用四进制数来存三进制状态,但这样直接开数组会爆炸,而且其中还有很多没有用的状态(不合法的括号序列),所以一开始我们dfs预处理一下状态,并给它们标号,用一个 unordered_map 来存状态到标号的对应。总的状态数上界大概是 \(\frac{\binom {n} {\frac{n}{2}}}{n+1}*2^\frac{n}{2}*(n+1)\) ,大约为 \(1.1\times 10^5\) ,其实是一个比较松的上界。(卡特兰数为合法括号序列个数,删掉其中的某一些对,最后还有一个位置可以放空。其实还应该容斥空的位置,不过这是一个上界而已)。

复杂度为 \(O(nm|S|)\) 。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long giant;
const int maxn=14;
const int maxg=1.2e5;
unordered_map<int,int> id;
int h[maxg],ids=0,a[maxn],mat[maxg][maxn],fx,fy,n,m;
giant f[maxg],g[maxg];
bool ok[maxn][maxn];
inline void match(int mt[]) {
static int sta[maxn];
int top=0;
for (int i=1;i<=m+1;++i) if (a[i]==1) sta[++top]=i; else if (a[i]==2) {
int x=sta[top--];
mt[x]=i;
mt[i]=x;
}
}
inline bool legal() {
int tot=0;
for (int i=1;i<=m+1;++i) if ((tot+=(!a[i]?0:(a[i]==1?1:-1)))<0) return false;
if (tot) return false;
return true;
}
inline int at() {
int ret=0;
for (int i=m+1;i;--i) (ret+=a[i])<<=2;
return ret;
}
void dfs(int now) {
if (now>m+1) {
if (legal()) {
int x=at();
h[id[x]=++ids]=x; // some id is empty!! it equals zero
match(mat[ids]);
}
return;
}
for (int i=0;i<3;++i) a[now]=i,dfs(now+1);
}
inline int get(int x,int p) {
return (x>>(p<<1))&3;
}
inline int mod(int x,int p,int d) {
int tmp=d<<(p<<1),aux=3<<(p<<1);
return (x&(~aux))+tmp;
}
vector<int> dec(int x) {
vector<int> ret;
ret.clear();
for (int i=1;i<=m+1;++i) ret.push_back((x>>(i<<1))&3);
return ret;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
dfs(1);
for (int i=1;i<=n;++i) {
static char s[maxn];
scanf("%s",s+1);
for (int j=1;j<=m;++j) if (s[j]=='.') {
ok[i][j]=true;
fx=i,fy=j;
}
}
f[id[0]]=1;
for (int i=1;i<=n;++i) {
swap(f,g);
memset(f,0,sizeof f);
for (int j=1;j<=ids;++j) {
int d=h[j];
if (get(d,m+1)==0) f[id[d<<2]]=g[j];
}
for (int j=1;j<=m;++j) {
swap(f,g);
memset(f,0,sizeof f);
for (int k=1;k<=ids;++k) {
int d=h[k],x=get(d,j),y=get(d,j+1);
if (!ok[i][j]) {
if (!x && !y) f[k]+=g[k];
} else {
if (x==0 && y==0) {
int v=mod(mod(d,j,1),j+1,2);
int w=id[v];
f[w]+=g[k];
} else if (x==1 && y==1) {
int v=mod(mod(d,j,0),j+1,0);
v=mod(v,mat[k][j+1],1);
int w=id[v];
f[w]+=g[k];
} else if (x==2 && y==2) {
int v=mod(mod(d,j,0),j+1,0);
v=mod(v,mat[k][j],2);
int w=id[v];
f[w]+=g[k];
} else if (x==0 || y==0) {
int v1=mod(mod(d,j,x+y),j+1,0);
int v2=mod(mod(d,j,0),j+1,x+y);
int w1=id[v1],w2=id[v2];
f[w1]+=g[k],f[w2]+=g[k];
} else if (x==1 && y==2) {
if (i==fx && j==fy) {
int v=mod(mod(d,j,0),j+1,0);
int w=id[v];
f[w]+=g[k];
}
} else if (x==2 && y==1) {
int v=mod(mod(d,j,0),j+1,0);
int w=id[v];
f[w]+=g[k];
}
}
}
}
}
printf("%lld\n",f[id[0]]);
return 0;
}

ural1519-Formula 1的更多相关文章

  1. URAL1519 Formula 1 —— 插头DP

    题目链接:https://vjudge.net/problem/URAL-1519 1519. Formula 1 Time limit: 1.0 secondMemory limit: 64 MB ...

  2. URAL1519 Formula 1 【插头dp】

    题目链接 URAL1519 题解 看题型显然插头\(dp\) 考虑如何设计状态 有这样一个方案 当我们决策到某个位置 轮廓线长这样 你会发现插头一定是相互匹配的 所以我们实际上可以把状态用括号序列表示 ...

  3. 2019.01.23 ural1519 Formula 1(轮廓线dp)

    传送门 轮廓线dpdpdp模板题. 题意简述:给一个放有障碍的网格图,问有多少种方法能使所有非障碍格子都在同一条哈密顿回路上面. 考虑用括号序列的写法来状压这个轮廓线. 用000表示没有插头,111表 ...

  4. [URAL1519] Formula 1 [插头dp入门]

    题面: 传送门 思路: 插头dp基础教程 先理解一下题意:实际上就是要你求这个棋盘中的哈密顿回路个数,障碍不能走 看到这个数据范围,还有回路处理,就想到使用插头dp来做了 观察一下发现,这道题因为都是 ...

  5. DP设状态 : 状压与线

    [NOIP2017]宝藏(状压) [AHOI2009]中国象棋(状压) [BZOJ1814] URAL1519 Formula 1(插头\(DP\)模板) 新链接 : Luogu5056 , dark ...

  6. HDU1964 Pipes —— 插头DP

    题目链接:https://vjudge.net/problem/HDU-1964 Pipes Time Limit: 5000/1000 MS (Java/Others)    Memory Limi ...

  7. FZU1977 Pandora adventure —— 插头DP

    题目链接:https://vjudge.net/problem/FZU-1977  Problem 1977 Pandora adventure Accept: 597    Submit: 2199 ...

  8. URAL1519:Formula 1——题解

    http://acm.timus.ru/problem.aspx?space=1&num=1519 https://vjudge.net/problem/URAL-1519 题目大意:给一个网 ...

  9. redmine computed custom field formula tips

    项目中要用到Computed custom field插件,公式不知道怎么写,查了些资料,记录在这里. 1.http://apidock.com/ruby/Time/strftime 查看ruby的字 ...

  10. salesforce 零基础开发入门学习(十五)salesforce中formula的使用(不含Date/Time)

    本文参考官方的formula介绍PDF:https://resources.docs.salesforce.com/200/latest/en-us/sfdc/pdf/salesforce_usefu ...

随机推荐

  1. 安装虚拟机&Linux命令学习

    安装虚拟机&Linux命令学习 基于VirtualBox虚拟机安装Ubuntu 1.下载安装VirtualBox 根据自己电脑(32位操作系统)的实际情况,我在网上找了相应的VirtualBo ...

  2. 手撕一个 Galgame 神器——Shub-Niggurath Project

    一.想法 Galgame 我们大概可以分为好用的 Galgame 和好玩的 Galgame,但是如果你把好玩的 Galgame 拿来用的话,有时候会十分让人着急.如果你躺在床上,一只手还在按压键盘实际 ...

  3. equals和==方法比较(二)--Long中equals源码分析

    接上篇,分析equals方法在Long包装类中的重写,其他类及我们自定义的类,同样可以根据需要重新equals方法. equals方法定义 equals方法是Object类中的方法,java中所有的对 ...

  4. Spring学习(二)-----eclipse新建spring项目

    一:准本工作(下载需要的jar包) 1.下载准备Spring-framework-4.2.0 链接为: http://repo.springsource.org/libs-release-local/ ...

  5. Swift入门基础知识

    var //代表变量,变量的值可以改变 let//代表常量类型不可改变 //声明常量heh类型Swift会自动根据你的值来自动判断该变量的类型也可以指定类型(个人感觉还是指定类型的比较好,可能会减少系 ...

  6. leetcode- 将有序数组转换为二叉搜索树(java)

    将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定有序数组: [-10,-3,0, ...

  7. 【Pthon入门学习】99乘法表

    学习知识点: 1. string.join(seq): 以string字符串作为分隔符,将seq的所有元素拼接成一个新的字符串 s = ['%d * %d = %d' % (y, 4, 4*y) fo ...

  8. 华为笔试——C++进制转换

    题目:2-62进制转换 题目介绍:输入一个n1 进制的整数(包括负数),将其转换成n2 进制,其中n1 .n2 的范围是 [ 2,62 ] .每个数字的范围是0-9.a-z.A-Z.不用考虑非法输入. ...

  9. How to pass an Amazon account review

    Have you ever sold products on Amazon? How about sold so much within the first week that amazon deci ...

  10. HttpServlet 详解(基础)

    HttpServlet详解 大家都知道Servlet,但是不一定很清楚servlet框架,这个框架是由两个Java包组成:javax.servlet和javax.servlet.http. 在java ...