今天居然是二分专场,然而蒟蒻的我直到考后才发现,太惨了。
今日得分:100+40+40
话不多说,我们来看题。
T1
算术
【问题描述】
已知一个长度为 N+2 的序列 A[0],A[1],….,A[N],A[N+1], 这个数列满足如下条件: A[i] = (A[i-1] + A[i+1])/2 - D[i] (0<i<n+1)
现在给出数列 D,A[0],A[N+1] , 请计算出 A[1] 的值,我们可以证明 A[1]可能的取值存在且唯一(有唯一解)
【输入格式】
第一行给出一个正整数 N 第二行和第三行每行一个实数,依次代表 A[0] 和 A[N+1] 第四行给出 N 个实数,代表 D[1] 到 D[N] 所有实数均保留两位小数
【输出格式】
一行,一个实数代表答案,保留两位小数,当且仅当你的答案和标准答案完全一致你才可以 获得这个测试点的分数
【输入样例 1】
1
50.50
25.50
10.15
【输出样例 1】
27.85
【数据规模约定】
对于 60%的数据,有 1≤N≤100。对于 100%的数据,有 1≤N≤3000, -1000≤A[i]≤1000
此题解法极多(因为较简单)
解法一:推式子
然后解一下Σ即可。
解法二:首先,我们知道,A[1]与A[n+1]之间,必然存在线性关系,于是我们设这个关系为y=kx+b。
用两个A[1]的值代入,得到两个A[n+1]的值,可以解出这个关系,再根据给出的A[n+1]得到A[1]。
解法三:首先,我们知道,A[1]与A[n+1]之间,必然存在线性关系,于是我们设A[1]的值为x。
根据要求,一路用kx+b的形式表示到A[n+1],解方程即可。
解法四:首先,我们知道,A[1]与A[n+1]之间,必然存在线性关系,于是我们无脑二分A[1],随便搞一搞就出来了。
T1AC代码(解法三)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<map>
using namespace std;
inline int re_ad()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();return x*f;
}
int n;
double D[3010],A0,An1,ans;
struct node{double xs,cs;}A[3010];
inline void gx(int i)
{
A[i+1].cs=1.0*2*(A[i].cs+D[i])-A[i-1].cs;
A[i+1].xs=1.0*2*A[i].xs-A[i-1].xs;
}
int main()
{
n=re_ad();scanf("%lf%lf",&A0,&An1);
for(int i=1;i<=n;i++)scanf("%lf",&D[i]);
A[0].xs=0;A[0].cs=A0;
A[1].xs=1;A[1].cs=0;
for(register int i=1;i<=n;i++)gx(i);
ans=(An1-A[n+1].cs)/A[n+1].xs;
printf("%.2lf",ans);
}
T2
比赛
【问题描述】
2^N 名选手正在打石头剪刀布比赛,比赛的规则是两两进行 1v1 淘汰赛,并在 N 轮后决出 唯一的胜利者。 每名选手有一个独一无二的实力分数,代表他在这 2^N 名选手中的排名(不存在并列的情 况)。 比赛的结果具有随机性,即:对于进行比赛的两名选手,如果两名选手的实力分数的差的绝 对值小于等于某个给定的常数 K,则两名选手都有可能获得比赛胜利。 否则,两名选手中 分数较低(意味着排名更靠前)的选手一定会取得比赛的胜利。也因此,比赛的最终胜利者 不一定是实力分数最小(排名最靠前)的选手。 给出 2^N 与 K,问:最少需要在这 2^N 个选手中排多少名(即实力分数最大),才有可能 获得整个比赛的最终胜利者?
【输入】
第一行两个整数,分别表示 2^N, K
【输出】
所求的答案。
【输入样例】
16 3
【输出样例】
11
【数据规模】
对于 20%的测试数据,1≤2^N≤8 对于 100%的测试数据,1≤2^N≤4096, 0≤K≤2^N
题解:首先,一个显然的事实,如果i号选手可以获得比赛的胜利,那么i-1号选手一定可以获得比赛胜利。于是我们可以二分答案。检验的方法是贪心,每次尽可能选取当前选手所有有可能战胜的未参赛选手中编号最小的一个,倒着一轮一轮复盘,如果所有比赛都可以复盘成功,则答案可行,否则不行。
中间选取的过程还可以用并查集来维护,能优化掉一遍枚举的时间(然而不写也能过)。
然而在考场上并没有按照轮数bfs来复盘,而是采用dfs复盘,结果只有40分,至今未想明白为什么不对,留坑待补。
update:其实很简单,如果按照dfs复盘的话,靠前的选手有可能会被提前在较小的轮数就被使用,但是我们应该让他尽量赢更多的轮数,所以应该bfs复盘。
T2AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<map>
#include<ctime>
using namespace std;
inline int re_ad()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();return x*f;
}
inline int ma(int x,int y){return x>y?x:y;}
inline int mi(int x,int y){return x>y?y:x;}
int n,k,ans,a[5010];
bool vis[5000];
struct node{int x,y;};
bool win(int x,int ti)
{
register bool fla;
queue<node>q;
register node z;
q.push((node){x,ti});vis[x]=true;
while(!q.empty())
{
z=q.front();q.pop();
if(!z.y)continue;
fla=true;
for(register int i=ma(1,z.x-k);i<=n;i++)
if(!vis[i]){q.push((node){i,z.y-1});vis[i]=true;fla=false;break;}
if(fla)return false;
--z.y;
q.push(z);
}
return true;
}
void solve()
{
register int l=1,r=n,mid,ti=0,x=n;
while(x>1)ti++,x>>=1;
while(l<r)
{
mid=l+r>>1;
memset(vis,0,sizeof(vis));
if(win(mid,ti)){l=mid+1;}else r=mid;
}
memset(vis,0,sizeof(vis));
if(!win(l,ti))l--;
cout<<l<<endl;return;
}
int main()
{
n=re_ad();k=re_ad();
solve();return 0;
}
T3
数字三角形
【问题描述】
你有一个底部宽为 2N-1 的金字塔,除了底层以外,每一个位置的数都是下面三个数的中位数。求塔顶的数是多少?
【输入格式】
第一行包含一个整数:N 第二行 2N-1 个正整数,依次表示数字三角形最底下一层的数字
【输出格式】
一行包含一个整数 x,表示所求的答案
【输入样例】
4
1 6 3 7 4 5 2
【输出样例】
4
【数据规模】
40 分 1≤n≤2000 60 分 1≤n≤100000 数据有一定的梯度
考场上只打了40分枚举的暴力。第一次见这种解法的题,确实缺少经验。
由于是二分专场,我们可以二分一下答案,将所有小于等于它的数看作1,大于它的数看作0,将原问题转化成对01序列的处理。
考虑到如果有连续两个及以上的相同数字,则向上的过程中它的所有部分都会保持不变,一直向上走,逐渐逼近中间(自己画个图就能明白)。
所以我们只要判断哪一串连续序列最靠近中间,就可以得到最顶端是0还是1。
至于有不同的两个序列到中间的距离一样,那是不存在的,因为序列长是奇数,如果有两组到中间的距离一样,那么我们用0和1在中间交替隔开,一定会和两边的某一个序列合并成一个更近的序列。
最后再特判一下全01交错的情况,即可通过本题。
(然而考场数据过水,直接输出中间三个数的中位数等各种乱搞方法都可以获得100分的好成绩)
T3AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<bitset>
#include<map>
using namespace std;
inline int re_ad()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();return x*f;
}
inline int ma(int x,int y){return x>y?x:y;}
inline int mi(int x,int y){return x>y?y:x;}
int n,a[200010],A[200010],cnt=0;struct node{int num,ii;}B[200010];
bool cmp(node x,node y){return x.num<y.num;}
inline bool check(int k)
{
for(register int i=0;i<n-1;i++)
{
if(a[n-i]<=k&&a[n-i-1]<=k||a[n+i]<=k&&a[n+i+1]<=k)return true;
if(a[n-i]>k&&a[n-i-1]>k||a[n+i]>k&&a[n+i+1]>k)return false;
}
return a[1]<=k;
}
int main()
{
n=re_ad();n=n*2-1;
for(int i=1;i<=n;i++)a[i]=re_ad(),B[i].num=a[i],B[i].ii=i;
sort(B+1,B+n+1,cmp);
for(int i=1;i<=n;i++)
{
if(B[i].num!=B[i-1].num)++cnt;
A[cnt]=B[i].num;a[B[i].ii]=cnt;
}
int l=1,r=cnt,mid;
n=n+1>>1;
while(l<r)
{
int mid=l+r>>1;if(check(mid))r=mid;else l=mid+1;
}
cout<<A[l]<<endl;return 0;
}
总结
今天的内容主要是二分和分治,考试题并不算太难,但是确实拓宽了我对题目的认识。今天课程中内容更加丰富,需要好好体会,认真琢磨。
还有如下一段话: