Problem A. 小水獭游河南
小水獭来到河南旅游,它认为一个字符串 s 是 HENAN 的当且仅当存在两个非. 空. 字符串 a 和 b 满 足如下三个条件:
• a 由小写字母组成,且 a 中每种字母只出现了一次。
• b 由小写字母组成,且 b 是回文串,也就是说将 b 翻转后得到的字符串和 b 相同。
• 将 a 和 b 顺序拼接得到的字符串和 s 相同,也就是说 s = a + b。
第一行包含一个整数 T(1 ≤ T ≤ 103),表示数据组数。 对于每组数据: 一行包含一个由小写字母组成的字符串 s(1 ≤ |s| ≤ 105),表示小水獭询问的字符串。 保证所有数据的 P|s| ≤ 105。 输出格式 对于每组数据: 输出一行包含一个字符串。如果 s 是 HENAN 的,输出 HE;否则输出 NaN。
输入 | 输出 |
3 henan hhnan ysmihoyocom | HE NaN NaN |
题解:(官方题解如下)
先遍历所有小写字母个数,如果小写字母大于2,再判断之后是否是回文串,不是直接输出"NaN",是回文串输出"HE",同时a和b均不为空字符串,整个字符串长度不能为1,(此时a为空),b不需特判(比如abc字符串,b="c",a="ab")
当时不是这样写的,特判了样例naan,abc,a,naanna
#include<bits/stdc++.h>
using namespace std;
#define int long long
int book[110000]={0};
int check(string ss,int a)
{
int i=a,j=ss.size()-1;
while(i<j)
{
if(ss[i]!=ss[j])
return 0;
else
{
i++;
j--;
}
}
return 1;
}
signed main()
{
string s;
int n,t;
cin>>t;
while(t--)
{
memset(book,0,sizeof book);
cin>>s;
n=s.size();
if(n==1)
{
cout<<"NaN\n";
}
else
{
int f=0;
for(int i=1;i<=26;i++)
{
int x=s[i-1]-'a';//字母对应的ASII码
book[x]++;
if(book[x]>=2)
{
break;
}
if(check(s,i))
{
cout<<"HE\n";
f=1;
break;
}
}
if(!f)
cout<<"NaN\n";
}
}
return 0;
}
Problem F. Art for Last
给定一个长度为 n 的非负整数序列 A1, A2, . . . , An,其中 n ≥ 2。 给定正整数 k,满足 2 ≤ k ≤ n。要求从 A1, A2, . . . , An 中选择 k 项 Ap1 , Ap2 , . . . , Apk (1 ≤ p1 < p2 < · · · < pk ≤ n),使得下式取得最小值: min 1≤i
第一行包含两个正整数 n,k(2 ≤ k ≤ n ≤ 5 × 105),表示非负整数序列 A 的长度及选取的项数。 第二行包含 n 个非负整数 A1, . . . , An(0 ≤ Ai ≤ 109),表示给定的序列 A。
输入 | 输出 |
13 7 1 1 4 5 1 4 1 9 1 9 8 1 0 | 0 |
4 2 114 514 1919 810 | 87616 |
6 3 121 117 114 105 107 111 | 12 |
题解:本题结果的最大值为2e18,显然不能用暴力解,但思路是对的,先排序,相邻的k个数中,最小值是相邻两个数的差,最大值为最后一个数-第一个数,要使他们乘积最小,最大值也必须最小,可以用rmq求相邻两个数的差的最小值,(rmq就是为了解决数组下标某一区间内的最值),用数组a[i+k-1]-a[i],求k个数中最后一个数-第一个数,结果找乘积的最小值。
#include <bits/stdc++.h>//rmq时间复杂度O(nlogn)。
#include<string.h>
#define int long long
using namespace std;
const int N=5e5+10;
int LogN=25,n,k;
int a[N],log1[N],f[N][25];
void init()
{
log1[0]=-1;
for(int i=1;i<N;i++)
log1[i]=log1[i/2]+1;
int i,j;
for(int i=1;i<n;i++)//根据题意定义f二维数组
f[i][0]=a[i+1]-a[i];
for(j=1;j<=log1[n];j++)
for(i=1;i+(1<<j)-1<=n;i++)
{
f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
}
}
int rmq(int x,int y)//求数组中下标从x到y内数值的最值
{
int s=log1[y-x+1];
return min(f[x][s],f[y-(1<<s)+1][s]);
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int mx=2e18;
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+n+1);
init();
for(int i=k;i<=n;i++)
{
int p=rmq(i-k+1,i-1)*(a[i]-a[i-k+1]);//第一个下标i-k+1,最后一个下标i,rmq是从第一个到i-1个最小的f[j][0],(f[j)[0]是相邻两个数的差值,即rmq是从第一个数到i-1个数相邻两个数最小的差值
mx=min(mx,p);
}
cout<<mx;
return 0;
}
rmq模板
int LogN=25,n; int a[N],log1[N],f[N][25]; void init() { log1[0]=-1; for(int i=1;i<N;i++) log1[i]=log1[i/2]+1; int i,j; //for(int i=1;i<n;i++)//根据题意定义f二维数组 //f[i][0]=a[i+1]-a[i]; for(j=1;j<=log1[n];j++) for(i=1;i+(1<<j)-1<=n;i++) { f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]); } } int rmq(int x,int y)//求数组中下标从x到y内数值的最值 { int s=log1[y-x+1]; return min(f[x][s],f[y-(1<<s)+1][s]); }
Problem H. Travel Begins
给定正整数 n, k,我们称实数列 a1, a2, . . . , ak 为关于 n 的 k–实数划分当且仅当 {ai} 满足下列条件: • Pk i=1 ai = n • ∀1 ≤ i ≤ k : 0 ≤ ai ≤ n 记 n 的所有 k–实数划分构成的集合为 Dk(n)。 对于实数 x,我们定义 [x] 为不大于 x 的最大整数,并定义 x 的小数部分为 {x} = x − [x]。定义四 舍五入函数如下: r(x) = [x] , {x} < 0.5 [x] + 1 , {x} ≥ 0.5 试求下述两式的值: min a∈Dk(n) (X k i=1 r(ai) ) , max a∈Dk(n) (X k i=1 r(ai) )
第一行包含一个整数 T(1 ≤ T ≤ 105),表示数据组数。 对于每组数据: 一行包含两个正整数 n, k(1 ≤ n, k ≤ 109)。
输入 | 输出 |
5 1 3 4 1 2 2 3 7 4 3 | 0 2 4 4 2 3 0 6 3 5 |
题解:这个题当时比赛的时候,看队友在写,我没看懂题,直接放弃了,比赛完自己又写了一遍,我看有别人评价这个题找规律,我试着找了一下,竟然给找出来了,写了两次就过了,第一次是因为小数开少了,g,第一次做出了题有点难受竟然,
如果y=1,min=max=x,是特例
其余情况
1.最小值
让y-1个数均为0.499999999999999(double可以表示到小数点后15位),用x-(y-1)*0.499999999999999就是最后一个数的值,因为前y-1个数小数部分<0.5,r(a[i])的和为0,最后一位(int)(w+0.5)就是最小值
2.最大值
让y-1个数都为0.5,x-(y-1)*0.5就是最后一个数的值,前y-1个数小数部分=0.5,r(a[i])的和为y-1,最后一位(int)(w+0.5),结果为二者相加,就是最大值
#include <bits/stdc++.h>
#include<string.h>
#define int long long
using namespace std;
const int N=5e5+10;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t,n;
cin>>t;
while(t--)
{
int mn,mx;
double x,y;
cin>>x>>y;
if(y==1)
cout<<x<<" "<<x<<"\n";
else
{
double e=x-(y-1)*0.499999999999999;
if(e<0.5)
mn=0;
else
mn=(int)(e+0.5);
double r=x*1.0/0.5;
if(r<y)
mx=r;
else
{
double u=y-1;
double w=x-0.5*(y-1);
mx=u+(int )(w+0.5);
}
cout<<mn<<" "<<mx<<"\n";
}
}
return 0;
}
注意long double 与double区别:
1.long double在不同的编译器里面可以是8,10,16字节,double只能是8字节,long double字节大,运行速度慢
2.long double可以表示更大的精度
3.long double 可以表示更多的浮点数
Problem C. Toxel 与随机数生成器
这个题很水,
就是说字符串不够随机,只需判断前100个字符重复的次数,小于100,就是随机的,反之不随机,代码很短
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
string s; cin >> s;
// 记录重复数量
int cnt = 0;
string tar = s.substr(0, 100);
for(int i = 0; i + 100 < s.size(); i ++)
{
if(tar == s.substr(i, 100)){
cnt ++;
i += 100;
}
}
if(cnt < 2) cout << "Yes\n";//这个题给的太小了,2都能过,也可以开大点
else cout << "No\n";
}
Problem B. Art for Rest
题意:将序列分为floor(n/k)段,每一段都升序排序,得到的新的序列必须整体都是升序排列求这样的k的个数
思路:左边前缀的最大值<右边后缀的最小值,判断每一个下标的这种情况
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define int long long
using namespace std;
const int N=1e6+10;
int a[N],dp[N],f[N];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n,l=0;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
dp[1]=a[1],f[n]=a[n];
for(int i=2;i<=n;i++)
dp[i]=max(a[i],dp[i-1]);//前半段最大值
for(int i=n-1;i>=1;i--)
f[i]=min(f[i+1],a[i]);//后半段最小值
for(int i=1;i<=n;i++)
{
int g=0;
for(int j=i+1;j<=n;j+=i)
{
if(dp[j-1]>f[j])
{
g=1;
break;
}
}
if(g==0)
l++;
}
cout<<l;
return 0;
}