7.5-7.11的几题
额,上一篇bolg没打完就关机了,哎一晚上的劳动成果啊
寻思着这也好就没写博客了,就来补一篇吧。不是我说,这几天的题目都是些什么题啊。毒瘤出题人真是丧心病狂,唉……我还没订正完,就选几题来讲讲吧。
Day1.T1.黑客的平均数 (average.cpp)
【时空限制】
时间限制:4s
空间限制:256MB
【问题描述】
Chty_syq是一名黑客,但是他运气不佳,选课时段来临时,他选的课全都掉了,因此他想要侵入浙江大学教务网报复那些抢他课的欧皇。
在入侵的过程中,Chty_syq得到了一串密钥,这个密钥是一个长度为n的序列,为了破解这个密钥,他必须求出序列中一段连续子序列的最大平均值,且这个连续子序列的长度不小于k。
众所周知,Chty_syq不屑于做这种简单的问题,于是他把这个问题扔给了你,如果你能解决这个体力活,他将不再嘲笑你。
【输入格式】
第一行两个正整数n,k。
第二行n个整数表示这个序列。
【输出格式】
一个浮点数表示答案,保留6位小数。
【输入输出样例1】
average .in | average.out |
---|---|
4 3 | 2.666667 |
3 4 1 2 |
【输入输出样例2】
average .in | average.out |
---|---|
8 6 | 7.000000 |
4 7 9 5 8 1 9 10 |
【数据范围】
对于30%的数据, n,k<=5000
对于100%的数据, n,k<=1e5,1<=ai<=5000
这题我一开始看就是很熟悉的样子,但是不知道在哪里看到过。
结果一通瞎搞之后反正是凉了。然后老师讲的时候woc这题二分??!!,一翻蓝皮书是原题。
每次二分答案枚举出平均数,注意使用实数域上的二分。然后将序列中每个数减去mid,最后问题转化成了求一段长度不小于k的和大于零的连续子段。
why?为什么可以用二分,仔细思考:若选出的平均值过大,则再大也就会使更多的a[i]再减去mid之后小于零;而若小了,则再小的方案一定也是合法的,减去mid后的数组会更大。因此答案符合单调性,可以二分。
这又该如何求解呢,到这步就很简单了,维护前缀和sum[i],选出一个minn为k位之前最小值,求sum[i]-minn最大值即可。也就是一个很套路的问题了。
贴上代码:
#include<bits/stdc++.h>
#define N 100010
using namespace std;
int n,k;
int a[N]={};
double b[N]={};
bool check(double x){
double s[N]={};
for(int i=1;i<=n;++i)s[i]=s[i-1]+b[i];//前缀和
double minn=1e9,ans=-1e9;
for(int i=k;i<=n;++i){
minn=min(minn,s[i-k]);
ans=max(ans,s[i]-minn);
} //很套路,就是这个k的长度范围需要考虑;
return ans>=0;
}
int main()
{
freopen("average.in","r",stdin);
freopen("average.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
double l=-1e9,r=1e9,e=1e-7;
while(l<r-e){
double mid=(l*1.0+r)/2;//二分枚举该平均值
for(int i=1;i<=n;++i)b[i]=a[i]-mid;//序列每个数减去该值
if(check(mid))l=mid;
else r=mid;
}
if(check(l))printf("%.6lf",l);
else printf("%.6lf",r);
return 0;
}
/*
8 6
4 7 9 5 8 1 9 10
*/
考试的时候莫名脑抽,也不知道写了一个什么东西,现在也忘了啦,总之被虐的很惨。
吐槽一句:这个题面也是的,sqy。。。hehe
Day2.T2.2. 中珂院的难题(kotori.cpp)
【 时空限制】
时间限制: 2s
空间限制: 256MB
【 问题描述】
“ 我已经无法获得幸福了, 因为我发觉, 我早已是幸福的人了。 ” ——珂朵莉
近日, 中国珂学院的研究员们遇到了一个难题, 他们手上有一个数列
an , 现在有 m 次
操作, 每次操作有三个参数 l, r, k ,表示选取一个区间[l,r], 对于区间中的每个元素 an 把
它加上一个组合数
C
(
i
−
l
+
k
,
k
)
C(i-l+k,k)
C(i−l+k,k), 求 m 次操作后得到的数列, 答案对10^9+7 取模。
“ 要是我们中有 OI 选手在就好了” 一位研究员如是说, 于是他们找到了身为 OI 选手
的你, 希望你能帮助他们解决这个问题。
【 输入格式】
第一行, 两个整数 n,m。
接下来 m 行, 每行 3 个整数 l,r,k 表示一次操作。
【 输出格式】
一行 n 个整数表示操作结束后的数列, 答案对109 7 取模。
【 输入输出样例 1】
kotori. in | kotori.out |
---|---|
5 1 | 1 1 1 1 1 |
0 0 0 0 0 | |
1 5 0 |
【 输入输出样例 2】
kotori. in | kotori.out |
---|---|
10 2 | 2 4 6 8 10 7 3 6 10 15 |
1 2 3 4 5 0 0 0 0 0 | |
1 6 1 | |
6 10 2 |
【 数据范围】
对于 50%的数据, n, m <=2000, k <=100
对于 100%的数据, n, m <= 10e5, k <= 100
hehe这就是毒瘤出题人的态度?sqy:是的。
先说说本题正解算法吧:高阶差分 哦原谅我才疏学浅,但是告诉你,之前我连这个名字都没听过。
(补坑)但实际上n阶差分就是将数列差分n次得到的序列。
我们知道这样的组合数序列进行 k + 1 k+1 k+1阶差分之后全部变为0。按照差分模板,我们每次在第 k+1 层差分的 l 位置加上 1然后处理一下 1~(k+1) 的 r+1 位置,消去前缀和对后面的影响ch也就是在第 j 层差分的 r+1 位置减去前面的前缀和.
所以最终序列的和可以表示为
最终我们需要求的,每个询问给出 n ! / ( ( n − m ) ! ∗ m ! ) n! /((n-m)!*m!) n!/((n−m)!∗m!),n,m上图给出。
这题我是分段求得,n在2000范围内时暴力,大于时用优化算法。这样可以卡过,不然又几个点死卡不过。
代码:
#include<bits/stdc++.h>
#define N 100200
#define ll long long
using namespace std;
int n,m;
ll jc[N][150]={};
ll a[N]={},b[150][N]={};
#define getchar() (S==T&&(T=(S=BB)+fread(BB,1,1<<15,stdin),S==T)?EOF:*S++)
char BB[1<<15],*S=BB,*T=BB;
int read()
{
int f=0,w=1; char c;
while(c=getchar(),c<=47||c>=58)w=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),c>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
return f*w;
}
void init(){
jc[0][0]=1;
for(int i=1;i<=100000;++i)
for(int j=0;j<=min(i,100);++j)
if(j!=0)jc[i][j]=jc[i-1][j-1]+jc[i-1][j],jc[i][j]%=1000000007;
else jc[i][j]=1;
}
void work(){
int x,y,z;
for(int i=1;i<=m;++i){
x=read();y=read();z=read();
for(int j=x;j<=y;++j)a[j]+=jc[j-x+z][z],a[j]%=1000000007;
}
for(int i=1;i<=n;++i)printf("%lld ",a[i]);
}
signed main()
{
freopen("kotori.in","r",stdin);
freopen("kotori.out","w",stdout);
n=read();m=read();
init();
for(int i=1;i<=n;++i)a[i]=read();
int x,y,z;
if(n<=2000){
work();
return 0;
}
for(int i=1;i<=m;++i){
x=read();y=read();z=read();
++b[z][x];
for(int j=0;j<=z;++j)b[j][y+1]-=jc[z-j+y-x][z-j];
}
for(int i=100;i>-1;--i)
for(int j=1;j<=n;++j)
b[i][j]+=b[i+1][j]+b[i][j-1],b[i][j]%=1000000007,b[i][j]+=1000000007,b[i][j]%=1000000007;
for(int i=1;i<=n;++i)printf("%lld ",(b[0][i]+a[i])%1000000007);
return 0;
}
/*
5 1
0 0 0 0 0
1 5 0
*/
呼,其实还有一堆题要写,但是时间不太够,就直接结束吧。
现在总结一下考试的经验。
-
首先,拿到题应该把题全看一遍。选择做哪题,哪题打暴力。这几天老是不懂合理分配。
-
一定要用纸笔写写画画公式手推什么,不然不能理解题目。
-
对拍,很重要,很好用,很有趣,很费时间。平安平安。
-
考试时状态一定要好,前天晚上早点睡,一定早睡。。。!!。。。不然会死相极惨
好,暂时就这么点。