1.完美的序列(sequence.cpp/c/pas)
时空限制
时间限制:
1s
空间限制:
64MB
题目描述
LYK认为一个完美的序列要满足这样的条件:对于任意两个位置上的数都不相同。然而并不是所有的序列都满足这样的条件。
于是LYK想将序列上的每一个元素都增加一些数字(当然也可以选择不增加),使得整个序列变成美妙的序列。
具体地,LYK可以花费
1
点代价将第
输入输出格式
输入格式:
第一行一个数
接下来一行
n
个数
输出格式:
一个数表示变成完美的序列的最小代价。
输入输出样例
输入样例:
4
1 1 3 2
输出样例:
3
说明
对于 30% 的数据 n≤5 。
对于 60% 的数据 n≤1000 。
对于 80% 的数据 n≤30000 , ai≤3000 。
对于 100% 的数据 n≤100000 , 1≤ai≤100000 。
solution
要代价尽可能小,可以贪心的去解决
开一个 exist 数组用来判断一个数是否在原序列中
用 a 数组记录原序列中重复的数
先把
a 从小到大排序,然后贪心的修改对于当前的 a[i] 肯定是要找不在原序列中并且大于 a[i] 的最小的数。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define MAXN 100010
template<typename T>
inline void input(T &x) {
x=0; T a=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
x*=a;
return;
}
int a[MAXN],cntA;
bool exist[MAXN<<1];
//exist数组开两倍大小是为了防止下面RE
//最坏的情况是100000个100000,那样最大会到199999,开两倍就好了。
int main() {
int n;
input(n);
for(int i=1,x;i<=n;i++) {
input(x);
if(exist[x]) {
a[++cntA]=x;
continue;
}
exist[x]=true;
}
if(cntA==0) {
printf("0");
return 0;
}
sort(a+1,a+cntA+1);
int ans=0,nowA=1;
for(int i=1;nowA<=cntA;i++) {
if(i<a[nowA]||exist[i]) continue;
ans+=i-a[nowA++];
}
printf("%d",ans);
return 0;
}
2.LYK与实验室(lab.cpp/c/pas)
时空限制
时间限制:
5s
空间限制:
64MB
题目描述
LYK在一幢大楼里,这幢大楼共有
n
层,LYK初始时在第
这幢大楼有一个秘密实验室,在第
b
层,这个实验室非常特别,对LYK具有约束作用,即若LYK当前处于
两个旅行方案不同当前仅当存在某一次按下电梯后停留的楼层不同。
输入输出格式
输入格式:
一行
4
个数,
输出格式:
一个数表示答案,由于答案较大,将答案对 1000000007 取模后输出。
输入输出样例
输入样例#1:
5 2 4 1
输出样例#1:
2
输入样例#2:
5 2 4 2
输出样例#2:
2
输入样例#3:
5 3 4 1
输出样例#3:
0
说明
对于 20% 的数据 n,k≤5 。
对于 40% 的数据 n,k≤10 。
对于 60% 的数据 n,k≤500 。
对于 90% 的数据 n,k≤2000 。
对于 100% 的数据 n,k≤5000 。
solution
设 f[i][j] 表示第 i 次按停在第
j 层的方案数。设 sum[i][j]=∑k=1jf[i][k] ,也就是 f[i][j] 的前缀和
首先明白一点,能到达楼层 j 的楼层肯定是一段区间
这个区间怎么算呢。看上面的式子。
|x−j|<|x−b| = (x−j)2<(x−b)2化简得到: x∗(2∗b−2∗j)<b2−j2
分类讨论一下:
当 b>j 时 (2∗b−2∗j)>0
所以原式 =x<b2−j22∗b−2∗j=x<b+j2
同理也可推出来当 b<j 时, x>b+j2
因为 x 是个整数,所以上面那两个式子可以在化简一下即:
当
b>j 时, x≤⌊b+j2⌋ ,其中 ⌊⌋ 表示向下取整。当 b<j 时, x≥⌈b+j2⌉ , 其中 ⌈⌉ 表示向上取整。
这只是区间的一个边界,另一个怎么办?
当 b>j 时,另一个取 1 ,因为可能不能从
1 到 j ,但是那样sum[i][1] 的值就会是 0 ,不会对sum[i][x] 的值有影响。同理,当 b<j 的时候,另一个直接取 n 。
所以就可以列出下面的
dp 方程:f[i][j]=sum[i−1][r]−sum[i−1][l−1]−f[i−1][j]
其中
l={1⌈b+j2⌉b>jb<j,r={⌊b+j2⌋nb>jb<j所以 dp 方程可以写成这样
- f[i][j]=⎧⎩⎨sum[i−1][⌊b+j2⌋]−f[i−1][j]sum[i−1][n]−sum[i−1][⌈b+j2⌉−1]−f[i−1][j]b>jb<j
最后 ans=∑i=1nf[k][i] ,当然还要mod
然而这样写出来怕是会MLE,所以把 f 和
sum 变成滚动数组。
code
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define MAXN 5010
#define mod 1000000007
template<typename T>
inline void input(T &x) {
x=0; T a=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
x*=a;
return;
}
int f[2][MAXN];
int sum[2][MAXN];
int main() {
int n,a,b,k;
input(n),input(a),input(b),input(k);
f[0][a]=1;
for(int i=a;i<=n;i++)
sum[0][i]=1;
for(int i=1;i<=k;i++) {
int now=i%2,last=(i+1)%2;
for(int j=1;j<=n;j++) {
int &dp=f[now][j];
if(j>b) {
int l=ceil((j+b+1)*0.5),r=n;//l这么算是因为浮点误差,不这么写会WA一两个点
dp=(sum[last][r]-sum[last][l-1]-f[last][j])%mod;
} else if(j<b) {
int r=floor((j+b-1)*0.5);
dp=(sum[last][r]-f[last][j])%mod;
}
if(dp<0) dp=(dp+mod)%mod;//因为是减法取模,注意变成负数
sum[now][j]=(sum[now][j-1]+dp)%mod;
}
}
int ans=0;
int end=k%2;
for(int i=1;i<=n;i++)
ans=(ans+f[end][i])%mod;
printf("%d",ans%mod);
return 0;
}
3.旅行(travel.cpp/c/pas)
时空限制
时间限制:
1s
空间限制:
64MB
题目描述
LYK想去一个国家旅行。这个国家共有
n
个城市,有些城市之间存在道路,我们假定这些道路长度都是
我们定义城市
我们定义整个国家的最短路为任意两个城市( A,B 与 B,A 算作不同的点对)之间的最短路长度的和。
然而这个国家正处于危乱之中,极有可能一条道路会被恐怖分子炸毁。
LYK想知道,万一某条道路被炸毁了,整个国家的最短路为多少。若炸毁这条道路后整个国家不连通了,那么就输出“INF”(不加引号)。
输入输出格式
输入格式:
第一行两个数 n,m 。
接下来
m
行,每行两个数
输出格式:
输出
m
行,第
输入输出样例
输入样例:
2 2
1 2
1 2
输出样例:
2
2
说明
对于
对于 40% 的数据 1≤n<m≤100 。
对于 70% 的数据 1≤n≤100 , n<m≤3000 。
对于再另外
10%
的数据对于所有节点
i(1≤i<n)
,存在一条边连接
i
与
对于再另外
10%
的数据对于所有节点
i(1≤i<n)
,存在一条边连接
i
与
对于再另外
10%
的数据对于所有节点
i(1≤i<n)
,存在一条边连接
i
与
solution
- no solution.
code
- no code
我会补上的