鞍山热身赛的题,也是去年多校原题

题目大意:

求n个数的排列中满足相邻两个数互质的排列的数量并取模

当时的思路就是状压dp.. dp[i][state]  state用二进制记录某个数是否被取走,i 表示当前序列末尾的数字

然后gcd状态转移

可是n是28,算了一下有几亿个状态。。没法做。。

回来之后找了题解发现可以用数学方法优化,于是搞了半天终于ac了

首先在这个问题中:

两个数是否互质只与他们的质因数有关,所以质因数相同的数是等价的,称作此问题的等价类

质因数找到这些等价类,并得到每个类中的数的数量是很容易的。。

所以只需要对这些等价类进行处理,最后对每个等价类再乘以数量的排列数就可以得到答案了。

不过此时有了数量,就不能用二进制状压了,应该采用哈希来状压。

研究了一会发现哈希状压和二进制状压差不多,只不过把基数从(1+1)^n变成了 (num[1]+1)*(num[2]+1)....也是很好理解的

这些状态处理完,发现对于n=28只有 5600000个状态了,等价类数是17 所以复杂度是17*5600000

一交MLE了。由于取模最大30000,把数组改为short,中间结果int防溢出,不爆内存了。

然后时限30s,以为可以过,结果又T了。。

于是又想了一会,发现17,19,23这三个数与其他任意一个数的互质。。所以他们与 1 是等价的

加了这个优化以后复杂度下降到约为 14*1800000

8800ms AC...

代码如下

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int prime[]={,,,,,,,,};
const int np=;
int state[];
int g[][];
int vi[];
int num[];
int base[];
short dp[][];
bool ok[];
int n,m,ns,st;
void ini()
{
scanf("%d%d",&n,&m);
memset(g,,sizeof(g));
memset(vi,,sizeof(vi));
memset(num,,sizeof(num));
ns=;
state[++ns]=;
num[ns]=;
for(int i=;i<=n;i++)
{
st=;
if(ok[i])
{
num[]++;
continue;
}
for(int j=;j<np;j++)
{
if(i%prime[j]==)
{
st|=(<<j);
}
}
if(!vi[st])
{
state[++ns]=st;
num[ns]=;
vi[st]=ns;
}
else
{
num[vi[st]]++;
}
}
for(int i=;i<=ns;i++)
{
for(int j=;j<=ns;j++)
{
if((state[i]&state[j])==)
g[i][j]=;
}
}
base[]=;
st=;
for(int i=;i<=ns;i++)
{
base[i+]=base[i]*(num[i]+);
st+=base[i]*num[i];
}
}
int getnum(int i,int x)
{
int res=(x%base[i+])/(base[i]);
return res;
}
int getstate(int i,int num)
{
return num*base[i];
}
void dfs(int t,int x)
{
if(t==)
{
dp[x][]=;
return ;
}
if(dp[x][t]!=-)
return;
dp[x][t]=;
for(int i=;i<=ns;i++)
{
if(g[x][i]&&getnum(i,t)>=)
{
dfs(t-base[i],i);
dp[x][t]=((int)dp[x][t]+dp[i][t-base[i]])%m;
}
}
return;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
int T;
scanf("%d",&T);
memset(ok,,sizeof(ok));
ok[]=;
ok[]=;
ok[]=;
while(T--)
{
ini();
memset(dp,-,sizeof(dp));
int ans=;
for(int i=;i<=ns;i++)
{
dfs(st-base[i],i);
ans=((int)ans+dp[i][st-base[i]])%m;
}
for(int i=;i<=ns;i++)
{
while(num[i]>)
{
ans=((int)ans*num[i])%m;
num[i]--;
}
}
printf("%d\n",ans);
}
}

hdu4623:crime 数学优化dp的更多相关文章

  1. 【转】斜率优化DP和四边形不等式优化DP整理

    (自己的理解:首先考虑单调队列,不行时考虑斜率,再不行就考虑不等式什么的东西) 当dp的状态转移方程dp[i]的状态i需要从前面(0~i-1)个状态找出最优子决策做转移时 我们常常需要双重循环 (一重 ...

  2. P2120 [ZJOI2007]仓库建设 斜率优化dp

    好题,这题是我理解的第一道斜率优化dp,自然要写一发题解.首先我们要写出普通的表达式,然后先用前缀和优化.然后呢?我们观察发现,x[i]是递增,而我们发现的斜率也是需要是递增的,然后就维护一个单调递增 ...

  3. 【学习笔记】动态规划—斜率优化DP(超详细)

    [学习笔记]动态规划-斜率优化DP(超详细) [前言] 第一次写这么长的文章. 写完后感觉对斜优的理解又加深了一些. 斜优通常与决策单调性同时出现.可以说决策单调性是斜率优化的前提. 斜率优化 \(D ...

  4. 洛谷P2365/5785 任务安排 题解 斜率优化DP

    任务安排1(小数据):https://www.luogu.com.cn/problem/P2365 任务安排2(大数据):https://www.luogu.com.cn/problem/P5785 ...

  5. bzoj-4518 4518: [Sdoi2016]征途(斜率优化dp)

    题目链接: 4518: [Sdoi2016]征途 Description Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站. Pine计划用m天到达T地 ...

  6. bzoj-1096 1096: [ZJOI2007]仓库建设(斜率优化dp)

    题目链接: 1096: [ZJOI2007]仓库建设 Description L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚.由于这座山处于高原内陆地区(干燥少雨),L ...

  7. 【Codeforces720D】Slalom 线段树 + 扫描线 (优化DP)

    D. Slalom time limit per test:2 seconds memory limit per test:256 megabytes input:standard input out ...

  8. 单调队列优化DP,多重背包

    单调队列优化DP:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列优化多重背包:http://blog.csdn ...

  9. [BZOJ3156]防御准备(斜率优化DP)

    题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3156 分析: 简单的斜率优化DP

随机推荐

  1. Truncate Table 用法

    TRUNCATE TABLE 删除表中的所有行,而不记录单个行删除操作. 语法 TRUNCATE TABLE name 参数 name 是要截断的表的名称或要删除其全部行的表的名称. 注释 TRUNC ...

  2. Gradle[0]依赖本地JAR和远程仓库JAR的配置

    1.对本地Jar的依赖配置 如果不知道Jar包的远程仓库地址,而项目中又要使用该Jar包,就需要进行本地设置. 例如,需要使用的Jar包为sigar.jar,则需要在项目根目录下建目录:libs,并把 ...

  3. HBase的rowkey的设计原则

    HBase是三维有序存储的,通过rowkey(行键),column key(column family和qualifier)和TimeStamp(时间戳)这个三个维度可以对HBase中的数据进行快速定 ...

  4. (转)pem, cer, p12 and the pains of iOS Push Notifications encryption

    转自:http://cloudfields.net/blog/ios-push-notifications-encryption/ The serious pains of setting up a ...

  5. 谈谈Ext JS的组件——布局的用法

    概述 在Ext JS中.包括两类布局:组件类布局和容器类布局.由于有些组件是有不同的组件组合而成的,如字段就由标题和输入框构成,他们之间也是存在布局关系的,而这就须要组件类布局来处理组件内自己特有的布 ...

  6. Java设计模式——Observer(观察者)模式

    在多个对象之间建立一对多的关系,以便当一个对象状态改变的时候.其它全部依赖于这个对象的对象都能得到通知,并被自己主动更新. 适用情况: 当一个抽象模型有两个方面,当中一个方面依赖于还有一方面. 将这二 ...

  7. linux diff具体解释

    diff是Unix系统的一个非常重要的工具程序. 它用来比較两个文本文件的差异,是代码版本号管理的基石之中的一个.你在命令行下,输入: $ diff <变动前的文件> <变动后的文件 ...

  8. 【ThinkingInC++】64、重载new和delete,来模仿内存的分配

    /** * 书本:[ThinkingInC++] * 功能:重载new和delete.来模仿内存的分配 * 时间:2014年10月5日14:30:11 * 作者:cutter_point */ #in ...

  9. 前端笔试题目小结--获取输入参数用户名;查询URL字符串参数

    编写一个JavaScript函数getSuffix,用于获得输入参数的后缀名.如输入abc.txt,返回txt. str1 = "abc.txt"; function getSuf ...

  10. 在浏览器中就可以写F#程序了,你试试吧

    学习F#有一种简单办法,不需要安装Visual Studio,在浏览器中就可以写F#了,非常方便,进入下面的链接,你可以试试. http://www.tryfsharp.org/Learn