2 条题解
-
0
P5919 题解
首先注意到一个排列 的
order等于它的所有环长的 ,因为每进行一次置换相当于每个环rotate一个位置,于是问题转化为求出 使得 最大。然后考虑这样的问题:要求
order恰好等于 ,排列最小长度。假设我们有一组解 ,可以使用调整法得到最优解:
- ,直接删去这个数,这说明不能有 ;
- ,可以将它分为所有的 ,这说明一个数不能有多个不同质因子;
- ,删去 ,这说明同一个质因子的幂只会出现一次。
于是设 ,则最优解为 。
由于互质,问题转化为求出 使得 最大。
然后就可以
dp了,考虑dp[i][j]表示用了前i个素数,长度上限为j,最大的答案。转移枚举当前素数不加入或者加入几次方的,乘起来就行,要记录路径。
这个答案可能很大,但是发现
dp的操作只涉及乘法以及max,可以对所有数取log计算,由于本题限制特殊不容易卡精度,double就够用了。还原路径时还要注意总长度不够要补 1。
最后一个问题就是,知道了所有 ,怎么构造字典序最小。
考虑从前往后依次填数,最前面肯定贪心填 作为第一个环,然后 作为第二个环……
因此把 数组从小到大排序然后填进去即可。
复杂度预处理 ,询问 。
代码
#define db double const int N=10004,M=200; int n; int isp[N],pr[N],cp; db cln[N]; vi prp[N]; pair<db,int>dp[M][N]; void pre(){ rept(i,2,N){ if(!isp[i]){ pr[cp]=i; prp[cp]=vi(1,0); for(int j=i;j<N;j*=i)prp[cp].pb(j); cln[cp++]=log(i); } rep(j,cp){ if(i*pr[j]>=N)break; isp[i*pr[j]]=1; if(i%pr[j]==0)break; } } rep(i,M)rep(j,N)dp[i][j]={.0,0}; rep(i,M-1){ db cc=cln[i]; rep(j,N){ rep(k,sz(prp[i])){ if(j+prp[i][k]>=N)break; Mx(dp[i+1][j+prp[i][k]],{dp[i][j].F+cc*k,prp[i][k]}); } } } } void run(){ int n; cin>>n; int cx=M-1,cy=n; vi ans; while(cx){ int k=dp[cx][cy].S; if(k)ans.pb(k); cx--;cy-=k; } rep(_,cy)ans.pb(1); sort(all(ans)); int cc=1; for(int i:ans){ rept(j,cc+1,cc+i)cout<<j<<" "; cout<<cc<<" "; cc+=i; } cout<<"\n"; } -
0
这题首先不难想到需要每个置换能形成环才能满足 成立。
然后 order 是所有环大小的 LCM。
令环的大小为 (这里为了方便令)其实就是把寻找最大的 使得 。
对于先考虑如何让一个固定的 使得字典序最小。
由于是字典序,可以考虑贪心,首先对于一组数 满足 )形成的环最小是 ,那么对于多个环,每次应该可以会到时就回到(可以理解成拿最小的一个环进行贪心)。
很显然,一定存在一个最优解使所有 互质(假设,则必然存在质因数 使且,不妨令 质因数分解中 的指数更大, 则 可以改成 个 ) 使和、LCM 不变,字典序会更优。
然后发现如果一个数 含有超过 个质因子它一定不优秀,设他的其中两个质因子为 其必然能写成 其中能保证 两部分都 所以 (若 则 ,则),则 可以改成 使和不变,LCM 不变差,字典序会更优。
综上, 为 或不同质数次方。
于是可以先筛出 所有的质数,
然后进行带路径记录的分组背包(可以理解成对于每个质数 在 、、 中选一个(当然贡献是相乘的,不是平常背包里的相加)。
这里有两个小细节 :
一,可以采用
long double来代替高精度,由于只需比较大小,不需输出,所以不需要那么高的精度(实测可以过)。二,不难发现比较大的质数并不会被选中,我们可以先让程序用 所有的质数进行 dp,然后再循环出 所有数中最大可能用到的最大质数 ,然后再用 所有的质数进行 dp 。
最后放一下代码:
#include <bits/stdc++.h> #define for1(i,n) for(i=1;i<=(n);i++) #define forlr(i,l,r) for(i=(l);i<=(r);i++) using namespace std; typedef long double ld; const int N=10005,D=10000; int z[N],cz,n,pre[75][N],T,c[N],cc; bool b[N]; ld dp[75][N]; int main(){ int i,j,k; forlr(i,2,D){ if(!b[i]) z[++cz]=i; if(cz==72) break; for(j=1;z[j]*i<=D;j++){ b[z[j]*i]=1; if(!(i%z[j])) break; } } forlr(i,0,D) dp[0][i]=1; for1(j,cz){ forlr(i,0,D) pre[j][i]=0,dp[j][i]=dp[j-1][i]; forlr(i,0,D) for(k=z[j];i+k<=D;k*=z[j]) if(dp[j][i+k]<dp[j-1][i]*k) pre[j][i+k]=k,dp[j][i+k]=dp[j-1][i]*k; } scanf("%d",&T); while(T--){ scanf("%d",&n);cc=0; for(i=cz;i;i--) if(pre[i][n]) n-=(c[++cc]=pre[i][n]); sort(c+1,c+cc+1); for1(i,n) printf("%d ",i); for1(j,cc){ forlr(k,i,i+c[j]-2) printf("%d ",k+1); printf("%d ",i);i+=c[j]; } puts(""); } return 0; }
- 1
信息
- ID
- 3741
- 时间
- 2000ms
- 内存
- 128MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者