洛谷P4769 冒泡排序
n <= 60w,∑n <= 200w,1s。
解:首先有个全排列 + 树状数组的暴力。
然后有个没有任何规律的状压...首先我想的是按照大小顺序来放数,可以分为确定绝对位置和相对位置两种,但是都不好处理字典序。
然后换个思路一位一位的考虑放哪个数。用一维来记录|i - pi| - 逆序对数 * 2,还有一维记录是否紧贴下限,一个二进制数记录之前填了哪些数。
当前填到哪一位可以通过二进制中1的个数统计出来。这样就是一个n32n的做法,可以过n <= 14的点,得到24分。
/**
* There is no end though there is a start in space. ---Infinity.
* It has own power, it ruins, and it goes though there is a start also in the star. ---Finite.
* Only the person who was wisdom can read the most foolish one from the history.
* The fish that lives in the sea doesn't know the world in the land.
* It also ruins and goes if they have wisdom.
* It is funnier that man exceeds the speed of light than fish start living in the land.
* It can be said that this is an final ultimatum from the god to the people who can fight.
*
* Steins;Gate
*/ #include <bits/stdc++.h> const int N = , MO = ; int p[N], n; inline void Add(int &a, const int &b) {
a += b;
while(a >= MO) a -= MO;
while(a < ) a += MO;
return;
} inline void out(int x) {
for(int i = ; i < n; i++) {
printf("%d", (x >> i) & );
}
return;
} namespace Sta {
const int DT = , M = ;
int f[DT * ][M][], cnt[M], pw[M];
inline void prework() {
for(int i = ; i < M; i++) {
cnt[i] = + cnt[i - (i & (-i))];
}
for(int i = ; i < M; i++) {
pw[i] = pw[i >> ] + ;
}
return;
}
inline void solve() {
memset(f, , sizeof(f));
f[DT][][] = ;
/// DP
int lm = ( << n) - , lm2 = n * (n - ) / ; /// lm : 11111111111
for(int s = ; s < lm; s++) {
for(int i = -lm2; i <= lm2; i++) {
/// f[i + DT][s][0/1]
if(!f[i + DT][s][] && !f[i + DT][s][]) continue;
//printf("f %d ", i); out(s); printf(" 0 = %d 1 = %d \n", f[i + DT][s][0], f[i + DT][s][1]);
int t = lm ^ s;
while(t) {
int j = pw[t & (-t)] + , temp = i + std::abs(cnt[s] + - j) - cnt[s >> (j - )] * + DT, t2 = s | ( << (j - ));
/// f[i + DT][s][1] -> f[std::abs(cnt[s] + 1 - j) - cnt[s >> (j - 1)] + DT][s | (1 << (j - 1))][1]
Add(f[temp][t2][1], f[i + DT][s][1]);
//printf("1 > %d ", temp - DT); out(t2); printf(" 1 = %d\n", f[temp][t2][1]);
if(j > p[cnt[s] + ]) {
Add(f[temp][t2][], f[i + DT][s][]);
//printf("0 > %d ", temp - DT); out(t2); printf(" 1 = %d\n", f[temp][t2][1]);
}
else if(j == p[cnt[s] + ]) {
Add(f[temp][t2][], f[i + DT][s][]);
//printf("0 > %d ", temp - DT); out(t2); printf(" 0 = %d\n", f[temp][t2][0]);
}
t ^= << (j - );
}
}
} printf("%d\n", f[DT][lm][]);
return;
}
} int main() {
//printf("%d \n", (sizeof(Sta::f)) / 1048576);
int T;
scanf("%d", &T);
Sta::prework();
while(T--) {
scanf("%d", &n);
for(int i = ; i <= n; i++) {
scanf("%d", &p[i]);
}
Sta::solve();
}
return ;
}
24分状压
然后我们必须开始找规律了......这里我是完全没思路(太过SB)
冷静分析一波发现,一个合法的排列等价于一个不存在长度为三的下降子序列的排列。
因为如果存在长度为3的下降子序列,那么中间那个元素一定有一次移动不是如它所想的。而不存在的话,我们就可以归纳,冒泡排序每一步都会减少一对逆序对,下降子序列一定不会变长。(全是口胡,意会就行了)
然后有一个44分的状压DP出来了,我没写...
80分n2DP,先考虑怎么求答案。枚举哪个位置开始自由,如果在i开始自由的话就要知道后面有多少种填法。所以我们需要一个从当前状态到终态的状态,而不是从初态到当前。
(看题解)发现如果前缀最大值为j,那么当前要么填比j小的最小的,要么填比j大的。因为如果填了比j小的又不是最小的,就会有一个长为3的下降子序列。
然后设fi,j表示还剩i位要填,能填的数中比max(1~i)大的有j个,填满的方案数。
于是有f[i][j] = ∑f[i - 1][k] = f[i][j - 1] + f[i - 1][j]
于是枚举在哪自由,然后把对应的j求出来。每个位置要加上fn-i,0~j-1
注意有两个地方要break,一个是后面没有比max(1~i)大的数了,还有就是当前填了长为3的下降子序列了。
100分:发现f这个东西其实就是格路径方案数......特别注意i < j的时候为0,所以有一条线不能跨过......
组合数学一波,就能O(1)计算f了。然后发现这个东西fn-i,0~j-1不就是fn-i+1,j-1吗?然后就完事了,nlogn。
/**
* There is no end though there is a start in space. ---Infinity.
* It has own power, it ruins, and it goes though there is a start also in the star. ---Finite.
* Only the person who was wisdom can read the most foolish one from the history.
* The fish that lives in the sea doesn't know the world in the land.
* It also ruins and goes if they have wisdom.
* It is funnier that man exceeds the speed of light than fish start living in the land.
* It can be said that this is an final ultimatum from the god to the people who can fight.
*
* Steins;Gate
*/ #include <bits/stdc++.h> typedef long long LL;
const int N = , MO = ; int p[N], n;
int fac[N << ], inv[N << ], invn[N << ]; inline LL C(int n, int m) {
if(n < || m < || n < m) return ;
return 1ll * fac[n] * invn[m] % MO * invn[n - m] % MO;
} inline void Add(int &a, const int &b) {
a += b;
while(a >= MO) a -= MO;
while(a < ) a += MO;
return;
} inline void out(int x) {
for(int i = ; i < n; i++) {
printf("%d", (x >> i) & );
}
return;
} namespace Sta {
const int DT = , M = ;
int f[DT * ][M][], cnt[M], pw[M];
inline void prework() {
for(int i = ; i < M; i++) {
cnt[i] = + cnt[i - (i & (-i))];
}
for(int i = ; i < M; i++) {
pw[i] = pw[i >> ] + ;
}
return;
}
inline void solve() {
memset(f, , sizeof(f));
f[DT][][] = ;
/// DP
int lm = ( << n) - , lm2 = n * (n - ) / ; /// lm : 11111111111
for(int s = ; s < lm; s++) {
for(int i = -lm2; i <= lm2; i++) {
/// f[i + DT][s][0/1]
if(!f[i + DT][s][] && !f[i + DT][s][]) continue;
//printf("f %d ", i); out(s); printf(" 0 = %d 1 = %d \n", f[i + DT][s][0], f[i + DT][s][1]);
int t = lm ^ s;
while(t) {
int j = pw[t & (-t)] + , temp = i + std::abs(cnt[s] + - j) - cnt[s >> (j - )] * + DT, t2 = s | ( << (j - ));
/// f[i + DT][s][1] -> f[std::abs(cnt[s] + 1 - j) - cnt[s >> (j - 1)] + DT][s | (1 << (j - 1))][1]
Add(f[temp][t2][1], f[i + DT][s][1]);
//printf("1 > %d ", temp - DT); out(t2); printf(" 1 = %d\n", f[temp][t2][1]);
if(j > p[cnt[s] + ]) {
Add(f[temp][t2][], f[i + DT][s][]);
//printf("0 > %d ", temp - DT); out(t2); printf(" 1 = %d\n", f[temp][t2][1]);
}
else if(j == p[cnt[s] + ]) {
Add(f[temp][t2][], f[i + DT][s][]);
//printf("0 > %d ", temp - DT); out(t2); printf(" 0 = %d\n", f[temp][t2][0]);
}
t ^= << (j - );
}
}
} printf("%d\n", f[DT][lm][]);
return;
}
} namespace Stable { int ta[N]; inline void add(int i) {
for(; i <= n; i += i & (-i)) ta[i]++;
return;
}
inline int ask(int i) {
int ans = ;
for(; i; i -= i & (-i)) {
ans += ta[i];
}
return ans;
}
inline void clear() {
memset(ta + , , n * sizeof(int));
return;
} inline bool check() {
int ans = , cnt = ;
for(int i = n; i >= ; i--) {
cnt += std::abs(i - p[i]);
ans += ask(p[i] - );
add(p[i]);
}
clear();
return cnt == ans * ;
} inline int cal() {
int ans = ;
for(int i = ; i <= n; i++) {
if(p[i] < p[i - ]) ans++;
}
return ans + ;
} inline void work() {
for(n = ; n <= ; n++) {
for(int i = ; i <= n; i++) p[i] = i;
do {
if(check()) {
for(int i = ; i <= n; i++) {
printf("%d ", p[i]);
}
}
else {
printf("ERR : ");
for(int i = ; i <= n; i++) {
printf("%d ", p[i]);
}
}
printf(" cal = %d \n", cal());
} while(std::next_permutation(p + , p + n + ));
getchar();
}
return;
}
} namespace DP {
const int M = ;
int f[M][M], ta[N];
inline void add(int i) {
for(; i <= n; i += i & (-i)) ta[i]++;
return;
}
inline int ask(int i) {
int ans = ;
for(; i; i -= i & (-i)) {
ans += ta[i];
}
return ans;
}
inline int getSum(int l, int r) {
return ask(r) - ask(l - );
}
inline int F(int i, int j) {
if(i < j) return ;
return (C(i + j - , j) - C(i + j - , i + ) + MO) % MO;
}
inline void solve() {
/*memset(f, 0, sizeof(f));
f[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= i; j++) {
f[i][j] = (f[i - 1][j] + f[i][j - 1]) % MO;
}
}*/ /*for(int j = n; j >= 0; j--) {
for(int i = 0; i <= n; i++) {
printf("%3d ", f[i][j]);
}
puts("");
}*/ int ans = , pre = ;
for(int i = ; i < n; i++) {
/// [1, i - 1] == i >
pre = std::max(pre, p[i]);
int k = n - pre - getSum(pre + , n);
//printf("i = %d k = %d \n", i, k);
if(!k) break;
/*for(int j = 0; j < k; j++) {
Add(ans, F(n - i, j));
//printf("add f %d %d \n", n - i, j);
}*/
Add(ans, F(n - i + , k - ));
add(p[i]);
if(p[i] < pre && ask(p[i]) != p[i]) break;
}
printf("%d\n", ans);
memset(ta + , , n * sizeof(int));
return;
}
} int main() {
//printf("%d \n", (sizeof(Sta::f)) / 1048576);
//Stable::work();
fac[] = inv[] = invn[] = ;
fac[] = inv[] = invn[] = ;
for(int i = ; i < N * ; i++) {
fac[i] = 1ll * fac[i - ] * i % MO;
inv[i] = 1ll * inv[MO % i] * (MO - MO / i) % MO;
invn[i] = 1ll * invn[i - ] * inv[i] % MO;
}
int T;
scanf("%d", &T);
//Sta::prework();
while(T--) {
scanf("%d", &n);
for(int i = ; i <= n; i++) {
scanf("%d", &p[i]);
}
DP::solve();
}
return ;
}
AC代码
洛谷P4769 冒泡排序的更多相关文章
- 【洛谷4769】[NOI2018] 冒泡排序(动态规划_组合数学)
题目: 洛谷 4769 博客页面左下角的嘴嘴瓜封神之战中的题目 分析: 一个排列交换次数为 \(\frac{1}{2}\sum_{i=1}^{n}|i-p_i|\) 的充要条件是这个排列不存在长度为 ...
- 【流水调度问题】【邻项交换对比】【Johnson法则】洛谷P1080国王游戏/P1248加工生产调度/P2123皇后游戏/P1541爬山
前提说明,因为我比较菜,关于理论性的证明大部分是搬来其他大佬的,相应地方有注明. 我自己写的部分换颜色来便于区分. 邻项交换对比是求一定条件下的最优排序的思想(个人理解).这部分最近做了一些题,就一起 ...
- P1075,P1138(洛谷)
今天难得做了做洛谷的题,而且还是两个! P1075:已知正整数n是两个不同的质数的乘积,试求出两者中较大的那个质数.输入格式:一个正整数n.输出格式:一个正整数p,即较大的那个质数. 第一版代码: # ...
- 快速排序--洛谷卡TLE后最终我还是选择了三向切割
写在前边 这篇文章呢,我们接着聊一下排序算法,我们之前已经谈到了简单插入排序 和ta的优化版希尔排序,这节我们要接触一个更"高级"的算法了--快速排序. 在做洛谷的时候,遇到了一道 ...
- Solution -「洛谷 P4372」Out of Sorts P
\(\mathcal{Description}\) OurOJ & 洛谷 P4372(几乎一致) 设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...
- 洛谷1640 bzoj1854游戏 匈牙利就是又短又快
bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...
- 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.
没有上司的舞会 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...
- 洛谷P1108 低价购买[DP | LIS方案数]
题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...
- 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP
题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...
随机推荐
- C# Note31: 如何使用Visual Studio做单元测试
待更! 使用Visual Studio 2013进行单元测试--初级篇 带你玩转Visual Studio——单元测试(C++例)
- Vue 鼠标移入移出事件
Vue 中鼠标移入移出事件 @mouseover和@mouseleave 然后绑定style 现在开始代码示例 <template> <div class="pc&qu ...
- RPC框架-RMI、RPC和CORBA的区别
关键词:RMI RPC CORBA简 介:本篇文章重点阐述RMI,附带介绍RPC和CORBA Java远程方法调用(Java RMI)是一组实现了远程方法调用(rmi)的API. java RMI是远 ...
- 二、kubernetes环境搭建
主要内容 1.环境准备(2主机) 2.安装流程 3.问题分析 4.总结 环境配置(2主机) 系统:CentOS 7.3 x64 网络:局域网(VPC) 主机: master:172.16.0.17 m ...
- Linq:使用Take和Skip实现分页
Skip,Take: list = list.Skip(pageNum * pageSize).Take(pageSize).ToList(); pageSize :表示一页多少条. pageNum: ...
- Vivado安装、生成bit文件及烧录FPGA的简要流程
https://wenku.baidu.com/view/0294cbb3bb4cf7ec4bfed01a.html
- .net core compatibility windows & windows compatible Linux
Who is this package for? This package is meant for developers that need to port existing .NET Framew ...
- 基于MMSE的预测
本文的目的是预测随机变量的输出值. 既然有预测值,那么我们就需要一个判断基准(criterion)用于判断该预测值与该随机变量的实际输出之间的差值,这里采用的判断基准就是MSE(mean-square ...
- BZOJ4946[Noi2017]蔬菜——线段树+堆+模拟费用流
题目链接: [Noi2017]蔬菜 题目大意:有$n$种蔬菜,每种蔬菜有$c_{i}$个,每种蔬菜每天有$x_{i}$个单位会坏掉(准确来说每天每种蔬菜坏掉的量是$x_{i}-$当天这种蔬菜卖出量), ...
- Codeforces1036F Relatively Prime Powers 【容斥原理】
题目分析: 这种题目标题写莫比乌斯反演会不会显得太恐怖了,那就容斥算了. gcd不为1的肯定可以开根.所以把根式结果算出来就行了. 辣鸡题目卡我精度. 代码: #include<bits/std ...