[洛谷P3672]小清新签到题

题目描述

题目还是简单一点好。

给定自然数n、k、x,你要求出第k小的长度为n的逆序对对数为x的1~n的排列a1,a2...an,然后用仙人图上在线分支定界启发式带花树上下界最小费用流解决问题,保证存在。

输入格式

一行三个自然数n、k、x。

输出格式

输出满足条件的排列,一行n个数,用空格分隔。

题目都说了:用仙人图上在线分支定界启发式带花树上下界最小费用流解决问题就可以了

看数据范围猜是DP......

我们可以用$f[i][j]$表示序列长度为i,其中有j对逆序对时的可能组数,转移也很显然:

$$f[i][j]=\sum_{k=max(0,i-j+1)}^j f[i-1][k]$$

大意就是累加i-1时有k对逆序对,放第i+1个数时因为放的地方不一样,新产生的逆序对个数也不一样,全部加起来即可

不过有几个问题

①虽然上面这个转移看起来是$O(n^3)$的,但是因为逆序对个数可以达到$\frac{n*(n-1)}{2}$个,所以其实是$O(n^4)$的

解决方案:观察转移方程我们可以发现$f[i][j]$必定是$f[i-1][j]$中的一段连续和,所以可以用前缀和优化

②显然这样做会爆$int$

解决方案:所以我们需要开$long long$

③显然这样做会爆$long long$

解决方案:我们设定一个$inf$值如果$f[i][j]$已经比$inf$大了继续累加就没有意义了,所以直接把$f[i][j]$设为$inf$即可

④显然开了两个大数组($f[i][j]$和$g[i][j]$)会被毒瘤出题人卡空间

解决方案:观察到$f[i][j]$和$g[i][j]$都之和前一个状态有关,那就把第一维压掉,但是$f[i][j]$在后面输出序列的时候是要用到的,所以压掉$g[i][j]$数组即可

 

DP结束了之后直接乱搞就好了,枚举每一位的大小$i$,比较$f[now][x-i+1]$和$k$的大小,如果$k>f[now][x-i+1]$就直接减掉,最后刚好凑齐的f[i][j]肯定就是最后答案了

 

 

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 #define writep(x)   write(x),putchar(' ')
 4 using namespace std;
 5 inline int read(){
 6     int ans=0,f=1;char chr=getchar();
 7     while(!isdigit(chr)){if(chr=='-') f=-1;chr=getchar();}
 8     while(isdigit(chr)){ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();}
 9     return ans*f;
10 }void write(int x){
11     if(x<0) putchar('-'),x=-x;
12     if(x>9) write(x/10);
13     putchar(x%10+'0');
14 }const int M = 3e2+5,inf=1e13+1;
15 int f[M][M*M>>1],a[M],n,x,k,ans[M],vis[M],cnt,g[2][M*M>>1];
16 void Solve(int now){
17     if(!now)return;
18     for(int i=1;i<=now;i++)
19         if(f[now-1][x-i+1]<k)k-=f[now-1][x-i+1];
20         else{ans[now]=a[i],x=x-i+1,vis[a[i]]=1;
21             for(int j=1,cnt=0;j<=n;j++)if(!vis[j])a[++cnt]=j;
22             return (void)(Solve(now-1));
23         }
24 }signed main(){
25     n=read(),k=read(),x=read();
26     for(int i=0;i<=n;i++)a[i]=i,f[i][0]=1;
27     for(int i=1;i<=n;i++){
28         for(int j=1;j<=(i*i-i)/2;j++)f[i][j]=min(inf,g[i&1^1][j+1]-g[i&1^1][max(j-i+1,0ll)]);
29         for(int j=0;j<=(n*n-n)/2;j++)g[i&1][j+1]=g[i&1][j]+f[i][j];
30     }Solve(n);
31     for(int i=n;i>=1;i--)writep(ans[i]);
32 return 0; 33 }

转载于:https://www.cnblogs.com/zhenglw/p/11470045.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的公寓报修管理系统,源码+数据库+毕业论文+视频演示 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本公寓报修管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此公寓报修管理系统利用当下成熟完善的Spring Boot框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。公寓报修管理系统有管理员,住户,维修人员。管理员可以管理住户信息和维修人员信息,可以审核维修人员的请假信息,住户可以申请维修,可以对维修结果评价,维修人员负责住户提交的维修信息,也可以请假。公寓报修管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 关键词:公寓报修管理系统;Spring Boot框架;MySQL;自动化;VUE
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值