【海高集训1】7.11总结

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 .inaverage.out
4 32.666667
3 4 1 2

【输入输出样例2】

average .inaverage.out
8 67.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(il+k,k), 求 m 次操作后得到的数列, 答案对10^9+7 取模。
“ 要是我们中有 OI 选手在就好了” 一位研究员如是说, 于是他们找到了身为 OI 选手
的你, 希望你能帮助他们解决这个问题。
【 输入格式】
第一行, 两个整数 n,m。
接下来 m 行, 每行 3 个整数 l,r,k 表示一次操作。
【 输出格式】
一行 n 个整数表示操作结束后的数列, 答案对109  7 取模。
【 输入输出样例 1】

kotori. inkotori.out
5 11 1 1 1 1
0 0 0 0 0
1 5 0

【 输入输出样例 2】

kotori. inkotori.out
10 22 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!/((nm)!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
*/

呼,其实还有一堆题要写,但是时间不太够,就直接结束吧。

现在总结一下考试的经验。


  • 首先,拿到题应该把题全看一遍。选择做哪题,哪题打暴力。这几天老是不懂合理分配。

  • 一定要用纸笔写写画画公式手推什么,不然不能理解题目。

  • 对拍,很重要,很好用,很有趣,很费时间。平安平安。

  • 考试时状态一定要好,前天晚上早点睡,一定早睡。。。!!。。。不然会死相极惨


好,暂时就这么点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值