首页 > 其他分享 >P3867题解

P3867题解

时间:2024-01-19 15:35:01浏览次数:19  
标签:ch 题解 a1 插入 a3 a2 P3867

P3867 [TJOI2009] 排列计数

题目传送门

题解

\(k\) 很小,不是分讨就是突破口。如果我们用这种方式生成排列:将 \(1\) 到 \(n\) 按顺序插入当前状态,那么你会发现当前的数 \(x\) 的插入被很大程度的限制住了,我们只需记录当前 \(x-k\) 到 \(x-1\) 的位置即可枚举出所有可能的下一状态,因此我们可以使用更新式 \(dp\)。

状态 \(f_{i,S}\) 表示长度为 \(i\) 的排列,当前 \(x-k\) 到 \(x-1\) 的位置为 \(S\) 的方案数,其中的 \(S\) 本质上是一个 \(k\) 维数组。

对于状态 \(f_{i,S_1,S_2,\cdots,S_k}\):如果 \(S\) 中有一个数在位置 \(1\),那就说明 \(i+1\) 可以被插入在 \(1\),因此更新 \(f_{i+1,1,S_1+1,S_2+1,\cdots,S_{k-1}+1}\)。末尾同理。如果存在 \(S_l=S_r-1\),那么 \(i+1\) 可以被插入在 \(S_r\),因此更新 \(f_{i+1,S_r,S_1',S_2',\cdots,S_{k-1}'}\),其中的 \(S'\) 需要考虑插入后会不会后移一位。

空间 \(128MB\) 可能会爆,把第一维滚动掉就好了。这样空间复杂度 \(O(n^k)\),时间复杂度 \(O(n^{k+1})\)。

好像就是这样了,虽然代码比较长,不过其实不难写。马蜂很乱,我也不准备解释了,如果你真的想看的话:

/*
 * @Author: operator_ 
 * @Date: 2024-01-16 13:50:22 
 * @Last Modified by: operator_
 * @Last Modified time: 2024-01-16 14:44:53
 */
#include<bits/stdc++.h>
using namespace std;
inline int rd() {
    int s=0,m=0;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-')m=1;ch=getchar();}
    while( isdigit(ch)) s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    return m?-s:s;
}
const int M=1e9+7;
#define U2 f[!i][p][a1+(a1>=p)]=(f[!i][p][a1+(a1>=p)]+f[i][a1][a2])%M
#define U3 f[!i][p][a1+(a1>=p)][a2+(a2>=p)]=(f[!i][p][a1+(a1>=p)][a2+(a2>=p)]+f[i][a1][a2][a3])%M
#define U4 f[!i][p][a1+(a1>=p)][a2+(a2>=p)][a3+(a3>=p)]=(f[!i][p][a1+(a1>=p)][a2+(a2>=p)][a3+(a3>=p)]+f[i][a1][a2][a3][a4])%M
int n,k,p,tmp[55],Ans;
signed main(){
    cin>>n>>k;
    if(k==1) return cout<<min(n,2),0;
    if(k==2) {
        int f[2][51][51];memset(f,0,sizeof(f));
        f[1][1][0]=1;
        for(int ii=1;ii<n;ii++) {
            int i=ii&1;memset(f[!i],0,sizeof(f[!i]));
            for(int a1=0;a1<=ii;a1++) {
                for(int a2=0;a2<=ii;a2++) {
                    tmp[a1]++,tmp[a2]++,tmp[0]=0;
                    if(tmp[a1]<2&&tmp[a2]<2) {
                        if(tmp[1]) p=1,U2;
                        if(tmp[ii]) p=ii+1,U2;
                        if(tmp[a1]&&tmp[a1+1]) p=a1+1,U2;
                        if(tmp[a2]&&tmp[a2+1]) p=a2+1,U2;
                    }
                    tmp[a1]--,tmp[a2]--;
                }
            }
        }
        for(int a1=0;a1<=n;a1++)
            for(int a2=0;a2<=n;a2++)
                Ans=(Ans+f[n&1][a1][a2])%M;
        return cout<<Ans,0;
    }
    if(k==3) {
        int f[2][51][51][51];memset(f,0,sizeof(f));
        f[1][1][0][0]=1;
        for(int ii=1;ii<n;ii++) {
            int i=ii&1;memset(f[!i],0,sizeof(f[!i]));
            for(int a1=0;a1<=ii;a1++) {
                for(int a2=0;a2<=ii;a2++) {
                    for(int a3=0;a3<=ii;a3++) {
                        tmp[a1]++,tmp[a2]++,tmp[a3]++,tmp[0]=0;
                        if(tmp[a1]<2&&tmp[a2]<2&&tmp[a3]<2) {
                            if(tmp[1]) p=1,U3;
                            if(tmp[ii]) p=ii+1,U3;
                            if(tmp[a1]&&tmp[a1+1]) p=a1+1,U3;
                            if(tmp[a2]&&tmp[a2+1]) p=a2+1,U3;
                            if(tmp[a3]&&tmp[a3+1]) p=a3+1,U3;
                        }
                        tmp[a1]--,tmp[a2]--,tmp[a3]--;
                    }
                }
            }
        }
        for(int a1=0;a1<=n;a1++)
            for(int a2=0;a2<=n;a2++)
                for(int a3=0;a3<=n;a3++)
                    Ans=(Ans+f[n&1][a1][a2][a3])%M;
        return cout<<Ans,0;
    }
    if(k==4) {
        int f[2][51][51][51][51];memset(f,0,sizeof(f));
        f[1][1][0][0][0]=1;
        for(int ii=1;ii<n;ii++) {
            int i=ii&1;memset(f[!i],0,sizeof(f[!i]));
            for(int a1=0;a1<=ii;a1++) {
                for(int a2=0;a2<=ii;a2++) {
                    for(int a3=0;a3<=ii;a3++) {
                        for(int a4=0;a4<=ii;a4++) {
                            tmp[a1]++,tmp[a2]++,tmp[a3]++,tmp[a4]++,tmp[0]=0;
                            if(tmp[a1]<2&&tmp[a2]<2&&tmp[a3]<2&&tmp[a4]<2) {
                                if(tmp[1]) p=1,U4;
                                if(tmp[ii]) p=ii+1,U4;
                                if(tmp[a1]&&tmp[a1+1]) p=a1+1,U4;
                                if(tmp[a2]&&tmp[a2+1]) p=a2+1,U4;
                                if(tmp[a3]&&tmp[a3+1]) p=a3+1,U4;
                                if(tmp[a4]&&tmp[a4+1]) p=a4+1,U4;
                            }
                            tmp[a1]--,tmp[a2]--,tmp[a3]--,tmp[a4]--;
                        }
                    }
                }
            }
        }
        for(int a1=0;a1<=n;a1++)
            for(int a2=0;a2<=n;a2++)
                for(int a3=0;a3<=n;a3++)
                    for(int a4=0;a4<=n;a4++)
                        Ans=(Ans+f[n&1][a1][a2][a3][a4])%M;
        return cout<<Ans,0;
    }
    return 0;
}

其实想简化的话办法也很多,一个最可行的是直接存 \(S\) 的哈希值,不过我懒了。

标签:ch,题解,a1,插入,a3,a2,P3867
From: https://www.cnblogs.com/operator-/p/17974764

相关文章

  • P7312题解
    P7312[COCI2018-2019#2]Sunčanje题目传送门题解分类讨论的思想有点像P4169?要你对于每一个矩形,求是否存在编号比它大,与它有交的矩形。直接做需要用一个比较神仙的线段树用法,所以我们可以容斥:我们求出编号比它大,与它无交的矩形数量,最后与所有可能覆盖它的矩形共\(n-i\)个......
  • P6554题解
    P6554PromisesICan'tKeep题目传送门题解看题解都有些做烦了,就来发一篇。换根dp。第一遍dfs处理出\(Lef_u\)表示\(u\)子树内的叶子个数(包含自己),然后求出以\(1\)为根时的答案\(\sumLef_u*val_u\),注意特判根为叶子的情况。第二遍dfs大力换根就好了,从根\(u\)......
  • P9744题解
    P9744「KDOI-06-S」消除序列题目传送门题解记错时间错过模拟赛的sb来也。题目中的最关键信息就是\(a_i,b_i,c_i\ge0\),这意味着多做无用的操作一定不优,所以有:结论\(1\):优先进行\(1\)操作。这是因为我们不管我们在\(1\)操作前做什么操作都会被其推平覆盖,是无用操......
  • P8047题解
    P8047[COCI2015-2016#4]GALAKSIJA题目传送门题解显然是要删边变加边的,然后联通性也是显然要用并查集维护的,就是路径异或和需要一个数据结构来维护。发现:加边删边不影响两个点的路径异或和。所以我们可以处理出每个点到\(1\)号节点的路径异或和\(d\),于是\(Path_{u,v}=d_u......
  • P8034题解
    P8034[COCI2015-2016#7]Ozljeda题目传送门题解评橙差不多了。手玩一下样例,很容易发现\(x\)的循环节为\(K+1\),每一段分别为\(a_1,a_2,a_3,\dots,a_K,\bigoplus_{i=1}^Ka_i\)这几项,然后恰好循环节的异或值为\(0\),所以就可以直接维护前缀异或值,然后取模求答案。代码:#i......
  • [AGC048D] Pocky Game 题解
    题目链接点击打开链接题目解法好难的题,想不出来一点!!!首先给出一个第一个结论:最优策略下,每个人每次只会取一个石子或者取完一堆石子题解区都没有严谨证明,\(at\)的\(editorial\)也没证,所以我只能感性理解:下面以先手为例。如果最左边的石子数\(>\)其余所有堆的石子数,那么先......
  • 题解 WD与数列
    P5161WD与数列可以想到原条件是一个差分形式,所以我们对原数组差分。然后发现答案其实就是\(\sum_{i<j}\min(lcp(i+1,j+1)+1,j-i)\)。这个东西先跑SA,然后建笛卡尔树。考虑对于一个区间,其值为\(x\)。那么相当于是求\(\sum_{l\inS,r\inT}\min(|sa_{l}-sa_{r}|,x)\)。笛......
  • AT_arc115_b [ARC115B] Plus Matrix 题解
    AT_arc115_b[ARC115B]PlusMatrix题解题意给定矩阵\(C_{n\timesn}\),求两个数列\(A_n,B_n\),使得\(C_{i,j}=A_i+B_j\)。分析画出一个表格来:213243502131324可以看出来,对于任意一列\(j\),\(C_{*,j}\)都存在有\(B_j\)的贡献。那么我们......
  • ELK之Filebeat自动断开问题解决
     自动断开命令 解决自动断开命令nohup./filebeat-e-cfilebeat.yml>./filebeat.log 2>&1& disown 其他的方式(目前我没有使用) 在linux操作系统/etc/systemd/system目录下创建一个filebeat.service文件,写入如下内容需要注意替换文件的位置以及版本[Unit]D......
  • GDKOI 2024 题解
    鸽了一些题。匹配先抽出来一个完美匹配,然后尝试调整。调整相当于:找一个偶环,满足匹配的边和未匹配的边交错,且偶环的总异或和为\(0\),是不是写个暴力就好了?发现冲过去了,很牛逼,复杂度\(O(n^3)\)(?),Code。不休陀螺一个区间可以被打出的条件是:令\(\Delta_i=b_i-a_i\),则\(x=\sum......