hdu4623:crime 数学优化dp

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

题目大意:

求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[]={2,3,5,7,11,13,17,19,23};
const int np=9;
int state[30];
int g[300][300];
int vi[300];
int num[30];
int base[30];
short dp[19][2000000];
bool ok[29];
int n,m,ns,st;
void ini()
{
    scanf("%d%d",&n,&m);
    memset(g,0,sizeof(g));
    memset(vi,0,sizeof(vi));
    memset(num,0,sizeof(num));
    ns=0;
    state[++ns]=0;
    num[ns]=1;
    for(int i=2;i<=n;i++)
    {
        st=0;
        if(ok[i])
        {
            num[1]++;
            continue;
        }
        for(int j=0;j<np;j++)
        {
            if(i%prime[j]==0)
            {
                st|=(1<<j);
            }
        }
        if(!vi[st])
        {
            state[++ns]=st;
            num[ns]=1;
            vi[st]=ns;
        }
        else
        {
            num[vi[st]]++;
        }
    }
    for(int i=1;i<=ns;i++)
    {
        for(int j=1;j<=ns;j++)
        {
            if((state[i]&state[j])==0)
                g[i][j]=1;
        }
    }
    base[1]=1;
    st=0;
    for(int i=1;i<=ns;i++)
    {
        base[i+1]=base[i]*(num[i]+1);
        st+=base[i]*num[i];
    }
}
int getnum(int i,int x)
{
    int res=(x%base[i+1])/(base[i]);
    return res;
}
int getstate(int i,int num)
{
    return num*base[i];
}
void dfs(int t,int x)
{
    if(t==0)
    {
        dp[x][0]=1;
        return ;
    }
    if(dp[x][t]!=-1)
        return;
    dp[x][t]=0;
    for(int i=1;i<=ns;i++)
    {
        if(g[x][i]&&getnum(i,t)>=1)
        {
            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,0,sizeof(ok));
    ok[17]=1;
    ok[19]=1;
    ok[23]=1;
    while(T--)
    {
        ini();
        memset(dp,-1,sizeof(dp));
        int ans=0;
        for(int i=1;i<=ns;i++)
        {
            dfs(st-base[i],i);
            ans=((int)ans+dp[i][st-base[i]])%m;
        }
        for(int i=1;i<=ns;i++)
        {
            while(num[i]>1)
            {
                ans=((int)ans*num[i])%m;
                num[i]--;
            }
        }
        printf("%d\n",ans);
    }
}
时间: 2024-10-30 04:35:02

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

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

当dp的状态转移方程dp[i]的状态i需要从前面(0~i-1)个状态找出最优子决策做转移时 我们常常需要双重循环 (一重循环跑状态 i,一重循环跑 i 的所有子状态)这样的时间复杂度是O(N^2)而 斜率优化或者四边形不等式优化后的DP 可以将时间复杂度缩减到O(N) O(N^2)可以优化到O(N) ,O(N^3)可以优化到O(N^2),依次类推 斜率优化DP和四边形不等式优化DP主要的原理就是利用斜率或者四边形不等式等数学方法 在所有要判断的子状态中迅速做出判断,所以这里的优化其实是省去了枚举

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

好题,这题是我理解的第一道斜率优化dp,自然要写一发题解.首先我们要写出普通的表达式,然后先用前缀和优化.然后呢?我们观察发现,x[i]是递增,而我们发现的斜率也是需要是递增的,然后就维护一个单调递增就行了. 放一个证明题解. 设f[i]表示在i点建仓库的最小费用,易得方程:f[i]=min(f[j]+(x[i]-x[j+1])*p[j+1]+(x[i]-x[j+1])*p[j+2]...) =min(f[j]+c[i]+x[i]*(p[j+1..i])-(x[j+1]*p[j+1]+...+x

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

题目链接: 4518: [Sdoi2016]征途 Description Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站. Pine计划用m天到达T地.除第m天外,每一天晚上Pine都必须在休息站过夜.所以,一段路必须在同一天中走完. Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小. 帮助Pine求出最小方差是多少. 设方差是v,可以证明,v×m^2是一个整数.为了避免精度误差,输出结果时输出v×m^2. In

HDU 4122 Alice&#39;s mooncake shop 单调队列优化dp

Alice's mooncake shop Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4122 Description The Mid-Autumn Festival, also known as the Moon Festival or Zhongqiu Festival is a popular harvest festival celebrated by Ch

[bzoj 1911][Apio 2010]特别行动队(斜率优化DP)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1911 分析: 首先可以的到裸的方程f[i]=max{f[j]+a*(Si-Sj)^2+b*(Si-Sj)+c} 0<j<i 简化一下方程,我们知道对于一次项,最后结果肯定是b*Sn 所以可以写成f[i]=max{f[j]+a*(Si-Sj)^2+c} 0<j<i 我们不妨设0<x<y<i,且x比y优 即f[x]+a*(Si-Sx)^2+c>f[y]+a*

hdu 2993 MAX Average Problem (斜率优化dp入门)

MAX Average Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5855    Accepted Submission(s): 1456 Problem Description Consider a simple sequence which only contains positive integers as

hdu3507之斜率优化DP入门

Print Article Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others) Total Submission(s): 4780    Accepted Submission(s): 1437 Problem Description Zero has an old printer that doesn't work well sometimes. As it is antiqu

Tyvj1305最大子序和(单调队列优化dp)

描述 输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大. 例如 1,-3,5,1,-2,3 当m=4时,S=5+1-2+3=7当m=2或m=3时,S=5+1=6 输入格式 第一行两个数n,m第二行有n个数,要求在n个数找到最大子序和 输出格式 一个数,数出他们的最大子序和 测试样例1 输入 6 4 1 -3 5 1 -2 3 输出 7 备注 数据范围:100%满足n,m<=300000 是不超过m,不是选m个!!!!! /* 单调队列优化dp 单调队列维护的是前

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

题目链接: 1096: [ZJOI2007]仓库建设 Description L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚.由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用.突然有一天,L公司的总裁L先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是L先生决定紧急在某些工厂建立一些仓库以免产品被淋坏.由于地形的不同,在不同工厂建立仓库的费用可能是不同的.第i个工厂目前已有成品Pi件,在第i个工厂位置建立仓库的费用是Ci.对