呃...心态崩了。
A 又是斐波那契数列??
时间限制 1s 内存限制 128Mb
大家都知道斐波那契数列吧?斐波那契数列的定义是这样的: f0 = 0, f1 = 1, fi = fi−1 + fi−2
现在给你一个数x,聪明的你一定知道这是斐波那契数列中的第几项。
(数据保证x一定有对应的项y,且 2 ≤ y < 1e4)
输入
第一行一个整数T,表示测试组数。
之后的T行,每行一个数x
输出
对于每个测试数据,输出一行表示数x是第几项。
输入样例
2
2
5
输出样例
3
5
思路:题意很明显,就是求斐波那契的前1e4项。但是斐波那契的增长速度很快,即使用double的1e308也只能存下很少的一部分,所以这个题的一般思路就是用数组进行大数加法。
另一种思路(从强大的学长那偷来的),由于斐波那契除了前面的几十项,其他的的每一项都很大,而且每一项的值也都不一样,因此我们可以用unsign long long来储存每一个斐波那契的值,当然,这个值肯定不是准确的斐波那契数列的值,在这个题里面,我们可以把它当作每一项的特征值(从1e18里面找到1e4个值,重复的概率微乎其微,从结果来看,确实没有重复的值)。
输入时,我们把输入的没一个数当做字符串,然后计算这个字符串(数)的值,输出对应的项数即可。
标准代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int N = (int) 100000 + 11;
const int M = (int) 1e6 + 11;
const int MOD = (int) 1e9 + 7;
const int INF = (int) 0x3f3f3f3f;
ull f[N]={0,1};
map<ull, int> mp;
int main(){
mp[1] = 1; mp[0] = 0;
for(int i = 2; i < N; i++){
f[i] = f[i-1] + f[i-2];
mp[f[i]] = i;
}
int T; cin>>T;
while(T--){
string s; cin>>s;
ull ans=0;
for(int i = 0; s[i]; i++)
ans = (ans * 10 + s[i] - '0');
//cout<<mp[ans]<<"\n";
}
return 0;
}
B Monotonic interval
时间限制 1s 内存限制 128Mb
设 f : P → Q 是在两个带有偏序 ≤ 的集合 P 和 Q 之间的函数。在微积分中,它们是带有平常 次序的实数集的子集之间的函数,但是定义仍保持同更一般的序理论定义一样。
函数 f 是单调的,如果只要 x ≤ y,则 f(x) ≤ f(y)。因此单调函数保持次序关系。
Neo 在研究某个一元函数 f(x) 的单调性,由于该函数太复杂了,没办法直接求导。但是能很容 易的知道 f(x) 在 x1, x2, . . . , xk 的取值为 f(x1), f(x2), . . . , f(xk) 。现在需要判断 f(x) 在区间 [l, r] 是否单调。
若 f(x) 在区间 [l, r] 可能单调,请输出“Possible”
若 f(x) 在区间 [l, r] 不可能单调,请输出“Impossible”
输入
第一行两个整数 k, q
第二行 k 个整数,第 i 个值代表 xi
第三行 k 个整数,第 i 个值代表 f(xi)
接下来 q 行,每行两个整数 l, r
对于所有的输入 0 < k, q ≤ 105,其他值的绝对值均不超过 109
数据保证: ∀i 6= j 都有 xi 6= xj
输出
请输出 q 行答案。
输入样例
5 3
1 2 3 4 5
5 4 3 4 5
1 3
2 4
3 5
输出样例
Possible
Impossible
Possible
思路:很明显,这个题&#*%*@……%&*%即可。
标准代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i, j, n) for(int i=j;i<n;i++)
#define per(i, j, n) for(int i=n;i>j;--i)
const int MAXN = (int)1e5+ 5;
const int MOD = 1000000007;
const int INF = 0x3f3f3f3f;
const double EPS = 1e-8;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
map<int, int> mp;
pii dat[MAXN];
int u[MAXN], d[MAXN], ip[MAXN];
int main() {
int k, q;
while(scanf("%d%d", &k, &q) != EOF) {
memset(u, 0x00, sizeof(u));
memset(d, 0x00, sizeof(d));
for(int i = 1; i <= k; ++i) {
scanf("%d", &dat[i].first);
}
for(int i = 1; i <= k; ++i) {
scanf("%d", &dat[i].second);
}
sort(dat + 1, dat + k + 1);
for(int i = 2; i <= k; ++i) {
u[i] += u[i - 1];
d[i] += d[i - 1];
if(dat[i].second < dat[i - 1].second) ++u[i];
if(dat[i].second > dat[i - 1].second) ++d[i];
}
for(int i = 1; i <= k; ++i) ip[i] = dat[i].first;
u[k + 1] = u[k];
d[k + 1] = d[k];
for(int i = 0; i < q; ++i) {
int l, r;
scanf("%d%d", &l, &r);
int ll = lower_bound(ip + 1, ip + 1 + k, l) - ip;
int rr = upper_bound(ip + 1, ip + 1 + k, r) - ip - 1;
puts(rr <= ll || u[rr] == u[ll] || d[rr] == d[ll] ? "Possible" : "Impossible");
}
}
return 0;
}
C Simplest
时间限制 1s 内存限制 128Mb
给你一段由数字0 − 9组成的字符串,请你输出它的最简自然数形式.
输入
第一行一个T,表示T组测试数据.(1 ≤ T ≤ 100)
每一组数据占一行,每一行一串字符 S. (1 ≤ strlen(S) ≤ 100000)
输出
输出字符串对应的最简自然数形式.
输入样例
2
2018722
02018722
输出样例
2018722
2018722
思路:obviously,这个题就是去除输入个字符串的前导0,然后输出字符串即可,这是一道签到题。
代码如下:
#include<stdio.h>
#include<string.h>
int main()
{
char str[100010];
int t,len,i;
scanf("%d",&t);
while(t--){
scanf("%s",str);
len=strlen(str);
for(i=0;i<len;i++){
if(str[i]!='0')
break;
}
if(i==len){
printf("0");
}
for(;i<len;i++){
printf("%c",str[i]);
}
printf("\n");
}
return 0;
}
D zz’s math problem Ⅰ
时间限制 1s 内存限制 32768kB
zz很喜欢数学,但是他又是一个数学渣,我们称这种生物叫做学渣.
zz又碰到了一个数学小问题,定义一个函数P(x) ,例如:P(123) = 1! ∗ 2! ∗ 3!
求在满足P(z) = P(x)的情况下,最大化z (x ≤ z, z 6= 1, z 6= 0)
输入
第1行输入T(1 ≤ T ≤ 20)组数据
第2行输入n(1 ≤ n ≤ 100),表示x数字的长度
第3行输入正整数x
输出
输出最大的z(数据保证x内含大于1的数,所以z必定有解)
输入样例
2
4
1234
3
555
输出样例
33222
555
HINT
第一个样例f(1234) = 1! ∗ 2! ∗ 3! ∗ 4! = 288 = f(33222)
思路:当时看的第一眼没什么思路,以为需要求每一个质数的个数啥的,后来发现想错了。
对于每个数,我们总是希望拆成更多的数字相乘,这样会保证最后的结果最长,也就是最后的结果最大。
对于1 2 3 5 7 ,这些数的阶乘没有办法分成更小的数字的阶乘了。
对于4!,我们可以分成2!*2!*3!,这样我们就把一个数字拆成了三个数字。
对于6!,我们可以拆成3!*5!;
对于8!,我们可以拆成2!*2!*2!*7!;
对于9!,我们可以拆成2!*3!*3!*7!。
这样,这个题基本上就解决了。
代码如下:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int main()
{
int p[10],len,i,j,t;
char str[110];
scanf("%d",&t);
while(t--){
memset(p,0,sizeof(p));
scanf("%d",&len);
scanf("%s",str);
for(i=0;i<len;i++){
p[str[i]-'0']++;
}
for(i=4;i<10;i++){
if(i==4){
p[2]+=p[4]*2;
p[3]+=p[4];
p[4]=0;
}
if(i==6){
p[3]+=p[6];
p[5]+=p[6];
p[6]=0;
}
if(i==8){
p[7]+=p[8];
p[2]+=3*p[8];
p[8]=0;
}
if(i==9){
p[7]+=p[9];
p[3]+=2*p[9];
p[2]+=p[9];
p[9]=0;
}
}
for(i=7;i>1;i--){
for(j=0;j<p[i];j++){
printf("%d",i);
}
}
printf("\n");
}
return 0;
}
E zz’s math problem Ⅱ
时间限制 1s 内存限制 32768kB
zz作为一个数学盲也认为这个数学题真的很简单,学弟学妹们终于可以顺利签到了qwq
给出N个正整数a1, a2, ..., aN , 我们寻找一个这个表达式的最大的值
f(m) = (m mod a1) + (m mod a2) + ... + (m mod aN )
mo的意思即为A/B的余数
输入
第1行输入T(1 ≤ T ≤ 20)组数据
第2行输入N(1 ≤ N ≤ 1e3)
第3行输入n个数字ai(1 ≤ ai ≤ 1e5)
输出
输出 f 的最大值
输入样例
1
3
3 4 6
输出样例
10
HINT
f(11) = (11 mod 3) + (11 mod 4) + (11 mod 6)的值10就是函数的最大值
思路:这个题也算是一到比较简单的签到题。
对于m mod a1,他的最大值为a1-1,以此类推。
因此,f(m)的最大值 (a1-1) + (a2-1) +...+(an-1),此时,m+1为a1,a2,...,an的公倍数。
代码如下:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int main()
{
int sum,t,n,a;
scanf("%d",&t);
while(t--){
sum=0;
scanf("%d",&n);
while(n--){
scanf("%d",&a);
sum+=a-1;
}
printf("%d\n",sum);
}
return 0;
}
F Operation on sequence
时间限制 1s 内存限制 512Mb
井底之蛙,偶见圆月,便欣然忘忧。
—《剑来》
为什么总是要为难学弟学妹呢,zz并不想这样,zz决定把出一道简单的题来福利学弟学妹们。
一组下标从1开始的数组s,进行q次操作:考虑两种操作:
1 r,将子序列a[1] 到 a[r] 从小到大排序
2 r,将子序列a[1] 到 a[r] 从大到小排序
输入
第一行输入T组数据 T(1 ≤ T ≤ 10)
第一行输入两个数字 n, q(1 ≤ n, q ≤ 1e4)
第二行包含n个整数 ai(−1e9 ≤ ai ≤ 1e9) — 初始序列
然后q行表示m个操作. 第i行包含两个整数 op(1 ≤ op ≤ 2), r(1 ≤ r ≤ n)
输出
输出n个数字,即数组被操作q次改变后的数组
注意,我们要输出的是最终改变后的结果
输入样例
2
3 1
1 2 3
2 2
4 2
1 2 4 3
2 3
1 2
输出样例
2 1 3
2 4 1 3
HINT
在第二组样例中初始序列是: 1 2 4 3.
执行第一次操作后的序列是: 4 2 1 3. 执行第二次操作后的序列是: 2 4 1 3
思路:题意很明显,就是排序,但是如果暴力排序的话,我们就会TLE了。因此,我们需要找到更简单的办法。
通过观察发现,对于下图
最大的r值对应的n值为7,因此,对于其前面的操作,我们可以不进行操作,因为7前面的操作都会被第七次操作覆盖。
然后继续对7后面的操作进行查询,最后我们会找到第11次操作,所以我们直接进行第11次操作,然后依次进行12,14,15次操作。
这样,我们可以将时间复杂度降到很低的程度。如果数据水的话,我们一次排序就可以直接出结果,当然,除非是出题人非常变态,卡你的数据,那就没有办法了。
代码如下:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct name
{
int op;
int r;
int pos;
}a[10010];
int cmp1(name i,name j){
if(i.r==j.r){
return i.pos>j.pos;
}
return i.r>j.r;
}
int cmp2(int a,int b){
return a>b;
}
int main()
{
int t,n,q,i,num[10010];
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&q);
for(i=0;i<n;i++){
scanf("%d",&num[i]);
}
for(i=0;i<q;i++){
scanf("%d %d",&a[i].op,&a[i].r);
a[i].pos=i;
}
sort(a,a+q,cmp1);
int max=0;
for(i=0;i<q;i++){
if(max<=a[i].pos){
if(a[i].op==1) sort(num,num+a[i].r);
else sort(num,num+a[i].r,cmp2);
max=a[i].pos;
}
}
for(i=0;i<n;i++){
if(i==0)
printf("%d",num[i]);
else
printf(" %d",num[i]);
}
printf("\n");
}
return 0;
}
G Operation on sequence
时间限制 1s 内存限制 512Mb
真·签到题
输入一个表达式A op B op操作为0+0 , 0 −0 , 0 ∗ 0 ,A, B为整数
输入
第一行输入T 组数据T(1 ≤ T ≤ 1e2)
第二行输入A op B (−1e9 ≤ A, B ≤ 1e9) — 初始序列
输出
输出表达式结果并换行即可
输入样例
1
1 + 1
输出样例
2
思路:真·签到题
代码如下:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int main()
{
char op;
int t;
long long a,b;
scanf("%d",&t);
while(t--){
scanf("%lld %c %lld",&a,&op,&b);
if(op=='+'){
printf("%lld\n",a+b);
}
else if(op=='-'){
printf("%lld\n",a-b);
}
else
printf("%lld\n",a*b);
}
return 0;
}
H 又是划分问题
时间限制 1s 内存限制 512Mb
给你一个正整数n,将其划分,要求划分成的数必须是2的幂,有多少种划分方法??
结果可能很大,我们输出对1e9+7取模的结果
输入
一个正整数n,代表要划分的数;
1 <= n <= 1e7
输入处理到文件结束
输出
输出可划分的方法数
输入样例
15
67
输出样例
26
2030
提示
当n=6时,我们可以将其划分为
1 1 1 1 1 1
1 1 1 1 2
1 1 2 2
2 2 2
1 1 4
2 4
这6种划分方法
思路:这个题是一道比较明显的dp题目,因此我们直接进行dp即可。
代码如下:
#include<cstdio>
const int N=1e7+10;
const int mod=1e9+7;
int s[N];
void solve()
{
s[1]=1;
for(int i=2;i<=N;i++)
{
if(i%2) s[i]=s[i-1];
else
s[i]=(s[i-1]+s[i/2])%mod;
}
}
int main()
{
int n;
solve();
while(~scanf("%d",&n))
printf("%d\n",s[n]);
return 0;
}
I 凸包与椭圆
时间限制 1s 内存限制 128Mb
在数学中,椭圆是围绕两个焦点的平面中的曲线,使得对于曲线上的每个点,到两个焦点的距 离之和是恒定的。因此,它是圆的概括,其是具有两个焦点在相同位置处的特殊类型的椭圆。 椭圆的形状(如何“伸长”)由其偏心度表示,对于椭圆可以是从0(圆的极限情况)到任意接 近但小于1的任何数字。
判断多个点组成的凸包是否与椭圆(x−x0) 2 a 2 + (y−y0) 2 b 2 = 1相交;若是,则输出Yes,否则输出No。 凸包就是把给定点包围在内部的、面积最小的凸多边形。
Figure 1: 示意图
输入
包含T组测试数据,对于每组测试数据,先输入一个整数n;接下来一行输入x0, y0, a, b;接下 来一行输入n个整数,代表n个点的横坐标;接下来一行输入n个整数,代表n个点的纵坐标; (x0, y0)代表椭圆的中心点。(a, b!=0),(3 ≤ n ≤ 1e5 )
输出
如果多个点组成的凸包与椭圆相交(误差保证在10−10),则输出Yes,否则输出No
思路:这个题目涉及的知识面非常广,凸包的求法,椭圆的性质,点到直线的距离等等。
由于这个题目太过复杂,我就不做深究了。
标准代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
struct Point{
double x,y;
Point(double x=0,double y=0):x(x),y(y){ }
};
typedef Point Vector;
Vector operator + (Vector A,Vector B){
return Vector(A.x+B.x,A.y+B.y);
}
Vector operator - (Vector A,Vector B){
return Vector(A.x-B.x,A.y-B.y);
}
const double eps=1e-10;
int dcmp(double x){
if(fabs(x)<eps) return 0;
else return x<0?-1:1;
}
Point p[100005],ch[100005];
bool cmp(Point A,Point B){
if(A.x==B.x) return A.y<B.y;
else return A.x<B.x;
}
double det(Point A,Point B){
return A.x*B.y-A.y*B.x;
}
int ConvexHull(Point* p,int n,Point* ch){
sort(p,p+n,cmp);
int m=0;
for(int i=0;i<n;i++){
while(m>1&&det(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
ch[m++]=p[i];
}
int k=m;
for(int i=n-2;i>=0;i--){
while(m>k&&det(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
ch[m++]=p[i];
}
if(n>1) m--;
return m;
}
double xx,yy,a,b;
bool check(Point A, Point B) {
if(dcmp(A.x - B.x) == 0) {
double delta = b * b - b * b * A.x * A.x / a / a;
if(delta < -eps) return false;
double y_1 = -sqrt(fabs(delta));
double y_2 = sqrt(fabs(delta));
return (dcmp(max(A.y, B.y) - y_2) >= 0 && dcmp(min(A.y, B.y) - y_2) <= 0 || (dcmp(min(A.y, B.y) - y_1) <= 0 && dcmp(max(A.y, B.y) - y_1) >= 0));
}
double k = (A.x - B.x) ? (A.y - B.y) / (A.x - B.x) : 1e100;
double g = -k * A.x + A.y;
double _a = b * b + a * a * k * k;
double _b = 2 * a * a * g * k;
double _c = a * a * (g * g - b * b);
double delta = _b * _b - 4 * _a * _c;
if(delta < -eps) return false;
double x_1 = (-_b - sqrt(fabs(delta))) / _a / 2.0;
double x_2 = (-_b + sqrt(fabs(delta))) / _a / 2.0;
return (dcmp(max(A.x, B.x) - x_2) >= 0 && dcmp(min(A.x, B.x) - x_2) <= 0 || (dcmp(min(A.x, B.x) - x_1) <= 0 && dcmp(max(A.x, B.x) - x_1) >= 0));
}
int main()
{
int T,n;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
scanf("%lf%lf%lf%lf",&xx,&yy,&a,&b);
for(int i=0;i<n;i++){
scanf("%lf",&p[i].x);
p[i].x-=xx;
}
for(int i=0;i<n;i++){
scanf("%lf",&p[i].y);
p[i].y-=yy;
}
int m=ConvexHull(p,n,ch);
int flag=0;
for(int i=0;i<m;i++){
if(check(ch[i],ch[(i+1)%m])){
flag=1;
break;
}
}
if(flag) printf("Yes\n");
else printf("No\n");
}
return 0;
}
J 台阶问题
时间限制 1s 内存限制 128Mb
有 N 级的台阶,你一开始在底部,每次可以向上迈最多 K 级台阶(最少 1 级),问到达第 N 级 台阶有多少种不同方式。
输入
多组输入,两个正整数N(N ≤ 1000),K(K ≤ 100)。
输出 一个正整数,为不同方式数,由于答案可能很大,你需要输出 ans mod 100003 后的结果。
输入样例
5 2
输出样例
8
思路:这也是一道很典型的dp题目,我们也是直接dp即可。
代码如下:
#include<cstdio>
#include<cstring>
int a[1000+10];
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k)){
memset(a,0,sizeof(a));
a[0]=1;
for(int i=1;i<=1000;i++){
for(int j=1;j<=k&&(i-j)>=0;j++){
a[i]+=a[i-j];
a[i]%=100003;
}
}
printf("%d\n",a[n]);
}
return 0;
}
K 括号括号
时间限制 1s 内存限制 128Mb
小明今年上大学,在大学里发现有很多同学都女朋友,两人整天都在一起腻歪,小明看到后感 觉很孤单,现在,给你一行括号序列,你来判断一下其中的括号是否配对。
第一行输入一个数N(0<n<=100),表示有N组测试数据。后面的N行输入多组输入数据,每组 输入数据都是一个字符串S(S的长度小于10000,且S不是空串),测试数据组数少于5组。数据保 证S中只含有”[”, ”]”, ”(”, ”)” 四种字符 ,输入以“EOF”结束。
输出
每组输入数据的输出占一行,如果该字符串中所含的括号是配对的,则输出Yes,如果不配对则 输出No。
输入样例
3
[(])
(])
([[]()])
输出样例
No
No
Yes
思路:这个题目字符串长度为1e4,因此我们直接暴力求解即可。
当我们找到一对[]或()时,我们用一些特殊符号标记一下,代表之后遍历的时候就不不需要再考虑这些字符。然后一直遍历到没有括号为止。或者使用栈进行操作。
代码如下:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<stack>
using namespace std;
int main()
{
char str[10010];
int len,t,i,j,n;
scanf("%d",&t);
while(t--){
stack<char>sta;
scanf("%s",str);
len=strlen(str);
sta.push(str[0]);
for(i=1;i<len;i++){
if(sta.top()=='(' && str[i]==')'){
sta.pop();
}
else if(sta.top()=='[' && str[i]==']'){
sta.pop();
}
else sta.push(str[i]);
}
if(sta.empty()){
printf("Yes\n");
}
else printf("No\n");
}
return 0;
}