Codeforces 1097E. Egor and an RPG game
首先考虑怎么算 $f(n)$ (就是题目里面那个 $f(n)$)
发现可以构造一组序列大概长这样: ${1,3,2,6,5,4,10,9,8,7,15,14,13,12,11,...,n(n+1)/2,n(n+1)/2-1,n(n+1)/2-2...n(n+1)/2-(n-1),(n+1)(n+2)/2,(n+1)(n+2)/2-1.....}$
然后发现这样构造的话,如果序列长度为 $k(k+1)/2$ ,那么至少需要分 $k$ 个数列
考虑证明这个即为上限,那么就是证明如果序列长度小于 $k(k+1)/2$ ,那么最多需要 $k-1$ 的数列
证明可以这样考虑,首先求出最长单调上升序列($LIS$),如果 $LIS$ 长度大于等于 $k$ ,那么可以直接把 $LIS$ 取出
然后序列长度就从小于 $k(k+1)/2$ 变成小于 $k(k+1)/2-k=(k-1)k/2$ ,相当于更小的子问题
所以如果序列长度在 $[(k-1)k/2,k(k+1)/2)$ 内,那么只要看看 $LIS$ 是否大于等于 $k$ 然后递归处理即可
现在问题是如果 $LIS$ 长度小于 $k$ 怎么办,发现我们一定可以用 $LIS$ 长度个下降子序列覆盖整个序列
构造下降子序列的方法我不太会说,看下面的一段代码或许比较清楚:
struct dat {
int id,val;
dat (int _id=,int _val=) { id=_id,val=_val; }
inline bool operator < (const dat &tmp) const {
return val!=tmp.val ? val<tmp.val : id<tmp.id;
}
};
void solve2(int mx)//mx是LIS长度
{
set <dat> S;
for(int i=;i<=mx;i++) S.insert(dat(m+i,N));
for(int i=;i<V.size();i++)//V 是当前序列,用vector存的
{
auto p = S.lower_bound(dat(-,V[i]));
ans[(*p).id].push_back(V[i]);
S.insert(dat((*p).id,V[i])); S.erase(*p);
}
m+=mx; V.clear();
}
考虑证明上面做法的正确性,容易想到反证法:
首先把当前的所有下降序列按此时末尾的数从小到大排序,那么对于第 $i$ 个下降序列末尾的数,它在原序列的位置一定比左边所有序列末尾的数都大
因为如果不是这样的话左边的数完全可以接到这个数的后面(仔细想想)
那么由于这个东西对于每个当前每个下降序列都成立,那么可以发现当前所有下降序列的末尾如果取出来刚好构成了一个上升序列,设这个上升序列为 $A$
(显然发现其实 $A$ 也是原序列的 $LIS$ 之一)
回到原来的问题,
如果出现不合法的数列,说明存在某一个位置它不管接在当前哪一个下降序列后都是上升的
那么说明它比当前所有下降序列的末尾都大,并且又因为它是当前位置最右的,那么它一定可以接在 $A$ 的后面,
发现此时就找到了一个长度大于 $LIS$ 的上升序列,所以矛盾了
然后就证明完了
所以按着上面的思路搞就行了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
typedef long long ll;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') { if(ch=='-') f=-; ch=getchar(); }
while(ch>=''&&ch<='') { x=(x<<)+(x<<)+(ch^); ch=getchar(); }
return x*f;
}
const int N=1e5+;
int n,m;
struct BIT {
int t[N];
inline void init() { for(int i=;i<=n;i++) t[i]=; }
inline void add(int x,int v) { while(x<=n) t[x]=max(t[x],v),x+=x&-x; }
inline int ask(int x) { int res=; while(x) res=max(res,t[x]),x-=x&-x; return res; }
}T;
int f[N];
vector <int> V,tmp,ans[N];
int work(int len)
{
T.init(); int mx=;
for(int i=;i<len;i++)
mx=max(mx, f[i]=T.ask(V[i])+ ),
T.add(V[i],f[i]);
return mx;
}
void solve1(int len,int mx)
{
tmp.clear(); int las=N; m++;
for(int i=len-;i>=;i--)
if(f[i]==mx&&V[i]<las)
ans[m].push_back(V[i]),mx--,las=V[i];
else tmp.push_back(V[i]);
reverse(ans[m].begin(),ans[m].end());
V=tmp; reverse(V.begin(),V.end());
}
struct dat {
int id,val;
dat (int _id=,int _val=) { id=_id,val=_val; }
inline bool operator < (const dat &tmp) const {
return val!=tmp.val ? val<tmp.val : id<tmp.id;
}
};
void solve2(int mx)
{
set <dat> S;
for(int i=;i<=mx;i++) S.insert(dat(m+i,N));
for(int i=;i<V.size();i++)
{
auto p = S.lower_bound(dat(-,V[i]));
ans[(*p).id].push_back(V[i]);
S.insert(dat((*p).id,V[i])); S.erase(*p);
}
m+=mx; V.clear();
}
int C2[N],h[N];
int main()
{
for(int i=;i<=;i++) C2[i]=i*(i+)/;
for(int i=;i<;i++)
for(int j=C2[i];j<C2[i+]&&j<N;j++)
h[j]=i;
int T=read();
while(T--)
{
for(int i=;i<=m;i++) ans[i].clear(); m=;
n=read(); for(int i=;i<=n;i++) V.push_back(read());
while(V.size())
{
int len=V.size(),mx=work(len);
if(mx>h[len]) solve1(len,mx);
else { solve2(mx); break; }
}
printf("%d\n",m);
for(int i=;i<=m;i++)
{
printf("%d ",int(ans[i].size()));
for(auto A: ans[i]) printf("%d ",A);
puts("");
}
}
return ;
}
Codeforces 1097E. Egor and an RPG game的更多相关文章
- Codeforces 1097E. Egor and an RPG game 构造
原文链接https://www.cnblogs.com/zhouzhendong/p/CF1097E.html 题解 首先我们求出 $k = f(n) = \max\{x|\frac{x(x+1)}2 ...
- 【CF1097E】Egor and an RPG game(动态规划,贪心)
[CF1097E]Egor and an RPG game(动态规划,贪心) 题面 洛谷 CodeForces 给定一个长度为\(n\)的排列\(a\),定义\(f(n)\)为将一个任意一个长度为\( ...
- CF1097E Egor and an RPG game
最少反链划分数 = 最长链.实现:每次找出所有极大元作为一个反链. 任意长度小于k * (k + 1) / 2的排列都能被划分为不多于k个单调序列.且这是一个紧的上界. 然后这题就可以切了. 题意:给 ...
- codeforces 1097 Hello 2019
又回来了.. A - Gennady and a Card Game 好像没什么可说的了. #include<bits/stdc++.h> using namespace std; cha ...
- Hello 2019 (D~G)
目录 Codeforces 1097 D.Makoto and a Blackboard(DP 期望) E.Egor and an RPG game(思路 LIS Dilworth定理) F.Alex ...
- Educational Codeforces Round 94 (Rated for Div. 2) String Similarity、RPG Protagonist、Binary String Reconstruction、Zigzags 思维
题目链接:String Similarity 题意: 首先题目定义了两个串的相似(串的构成是0.1),如果两个串存在对于一个下标k,它们的值一样,那么这两个串就相似 然后题目给你一个长度为2n-1的串 ...
- Educational Codeforces Round 94 (Rated for Div. 2) B. RPG Protagonist (数学)
题意:你和你的随从去偷剑和战斧,你可以最多可以拿\(p\)重的东西,随从可以拿\(f\)重的东西,总共有\(cnt_{s}\)把剑,\(cnt_{w}\)把战斧,每把剑重\(s\),战斧重\(w\), ...
- CodeForces 527B Error Correct System
Error Correct System Time Limit:2000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I6 ...
- (CodeForces 510C) Fox And Names 拓扑排序
题目链接:http://codeforces.com/problemset/problem/510/C Fox Ciel is going to publish a paper on FOCS (Fo ...
随机推荐
- Sign APK without putting keystore info in build.gradle
http://stackoverflow.com/questions/20562189/sign-apk-without-putting-keystore-info-in-build-gradle/2 ...
- spring boot定时任务
介绍 该demo是基于注解(@Scheduled)以及多线程执行的定时任务. 步骤 启用异步执行 springboot实现异步调用 入口类添加启动注解 @EnableScheduling @Enabl ...
- koa 项目中引入 mysql
由于mysql模块的操作都是异步操作,每次操作的结果都是在回调函数中执行,现在有了async/await,就可以用同步的写法去操作数据库 Promise封装mysql模块 Promise封装 ./as ...
- Flutter移动电商实战 --(20)首页上拉加载更多功能的制作
这节课学习一下上拉加载效果,其实现在上拉加载的插件有很多,但是还没有一个插件可以说完全一枝独秀,我也找了一个插件,这个插件的优点就是服务比较好,作者能及时回答大家的问题.我觉的选插件也是选人,人对了, ...
- SEO优化篇——meta用法
一.语法:<meta name="name" content="string"> 二.参数解析:1)name选项:Keywords(关键字),des ...
- JAVA 基础编程练习题45 【程序 45 被 9 整除】
45 [程序 45 被 9 整除] 题目:判断一个素数能被几个 9 整除 package cskaoyan; public class cskaoyan45 { public static void ...
- [转]将西部数据 My Passport Wireless 移动存储连接到任何支持的云存储上
原文标题:对西部数据 My Passport Wireless 移动存储进行 Linux 魔改 原文链接:https://linux.cn/article-8246-1.html 虽然 WD My P ...
- 取消一个本地svn目录与svn的联系
第一种方法: 直接.逐级地删除目标目录中的隐藏属性的.svn目录 这个方法试了,好牛蛋...,省事,快速... 第二种方法: 如果用的是TortoiseSVN客户端,则先在另外一处建立一个新目录A,右 ...
- 【Unsolved】线性时间选择算法的复杂度证明
线性时间选择算法中,最坏情况仍然可以保持O(n). 原因是通过对中位数的中位数的寻找,保证每次分组后,任意一组包含元素的数量不会大于某个值. 普通的Partition最坏情况下,每次只能排除一个元素, ...
- HelloCube:IJobForEach
此示例演示了基于作业的ECS系统,该系统可旋转一对立方体. 它显示了什么? 此示例基于ForEach示例构建,并说明如何在多线程作业中执行相同的工作,而不是在主线程上执行相同的工作. 与前面的示例一样 ...