CSP-M2
A - HRZ的序列
题目:
Sample:
input
2
5
1 2 3 4 5
5
1 2 3 4 5
output
NO
NO
思路:
set
不要被题目唬住了。满足条件的只有三种情况:
其一,只有一个数字多次重复;
其二,两个数字多次重复;
其三,三个数字多次重复,设这三个数为a,b,c(a<b<c),满足b-a=c-b。
总结:
set:自动去重,自动排序。
set大法好。
代码:
#include <iostream>
#include <set>
using namespace std;
long long t,n,a;
set<long long> s;
int main()
{
cin>>t; //数据组数
while(t--)
{
cin>>n; //序列a长度
for(int i=1;i<=n;i++)
{
cin>>a;
s.insert(a);
}
if(s.size()>3)
cout<<"NO"<<"\n";
else if(s.size()==1||s.size()==2)
cout<<"YES"<<"\n";
else if(s.size()==3)
{
long long t[10];int cnt=0;
for(set<long long>::iterator it=s.begin();it!=s.end();it++)
{
t[++cnt]=*it;
}
if((t[2]-t[1])==(t[3]-t[2]))
cout<<"YES"<<"\n";
else
cout<<"NO"<<"\n";
}
s.clear();
}
return 0;
}
B - HRZ学英语
题目:
Sample:
input
//sample 1
ABC??FGHIJK???OPQR?TUVWXY?
//sample 2
AABCDEFGHIJKLMNOPQRSTUVW??M
output
//sample 1
ABCDEFGHIJKLMNOPQRSTUVWXYZ
//sample 2
-1
思路:
滑动窗口
这数据量判断字符串长度个26个字符的子串肯定超时,复杂度得在O(N)。
拿一个vis[]
数组记录这个字母是否出现过。拿队列存结果子串。拿两个变量num
、que
记录字母的数量和’?'的数量。然后遍历字符串,遍历到的字符有三种情况:
其一,A-Z,且没有出现过,入队,标记字母,字母数加一;
其二,?,入队,问号数加一;
其三,A-Z,出现过,队列进行出队操作,直到当前字符对应的vis
不为1(表示在结果子串中未出现过),此时回归上面两种情况,按情况处理。注意pop
的时候要相应地处理vis
,num
,que
。
每加入一个字符,判断num+que
是否等于26,等于则得到结果子串,退出循环。最后判断满足条件的子串是否存在,不存在输出-1,存在则输出子串。
输出子串时,一直对队列做pop
操作,如果是A-Z,直接输出,如果是’?’,去vis[]
数组里找第一个为0的标号,输出对应的字母。
总结:
第一反应,滑动窗口!
然后,滑动窗口怎么处理?…两指针?…队列?…是不是还要有个数组来记录子串里26个字母的状态?
一 片 混 乱 ,满 脑 浆 糊 。
最后暴力拿了40分…
还是这个道理:ac了不代表掌握了。
代码:
#include <iostream>
#include <string>
#include <queue>
using namespace std;
string s;
queue<char> q;
bool vis[30];
int main()
{
cin>>s;
int num=0,que=0;
bool flag=0;
for(int i=0;i<s.size();i++)
{
if(s[i]!='?'&&vis[s[i]-'A']==0)
{
q.push(s[i]);
vis[s[i]-'A']=1;
num++;
}
else if(s[i]=='?')
{
q.push(s[i]);
que++;
}
else if(vis[s[i]-'A']==1) //不符合条件
{
while(vis[s[i]-'A']!=0)
{
char t=q.front();
q.pop();
if(t=='?')
que--;
else
{
num--;
vis[t-'A']=0;
}
}
q.push(s[i]);
vis[s[i]-'A']=1;
num++;
}
if(num+que==26)
{
flag=1;
break;
}
}
if(flag)
{
int i=0;
while(!q.empty())
{
if(q.front()!='?')
{
cout<<q.front();
q.pop();
}
else
{
while(vis[i]!=0)
i++;
cout<<(char)(i+'A');
q.pop();
i++;
}
}
}
else
cout<<"-1"<<endl;
return 0;
}
D - 咕咕东的奇妙序列
题目:
Sample:
input
5
1
3
20
38
56
output
1
2
5
2
0
思路:
找规律/前缀和(*)
---第1块,公差为 1 (1位数)-----------------------
1
1 2
1 2 3
......
1 2 3 4 5 6 7 8 9
---第2块,公差为 2 (2位数)----------------------
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10 11
......
1 2 3 4 5 6 7 8 9 10 11 ... 98 99
---第3块,公差为 3 (3位数)--------------------------------
1 2 3 4 5 6 7 8 9 10 11 ... 98 99 100
1 2 3 4 5 6 7 8 9 10 11 ... 98 99 100 101
......
1 2 3 4 5 6 7 8 9 10 11 ... 98 99 100 101 ... 998 999
---第4块,公差为 4 (4位数)--------------------------------
1 2 3 4 5 6 7 8 9 10 11 ... 98 99 100 101 ... 998 999 1000
......
由此可得,每一块的各行长度形成了一组等差数列。对于第i块来说,公差d
=i,记录b
为每一块的最后一行的长度,即b=b'+i*n
(b’为上一块的b,n为i位数的个数),那么每一块的首项a1
就是b+i
(这里的b
是上一行的b
)。那么就可以根据首项a1
,公差d
和项数n
来计算这个块的长度。
当x<=a-1
(代码里的else),表示x
在这个块里,这时,要计算这个块的第一行到x
所属的这一行的长度,只需要修改项数就可以了,其原理与计算块的长度相同。
对于一个数x
,可以通过上述操作来获取这个数所属的行,所以,可以通过二分得到k
所属的上一行的最大值ans
。那么,ans
的这一行的数就是从1至ans,k
在这之间。
对于1-ans,若要知道数x
在其中的位置,只要将1至x中的一位数个数乘一,两位数个数乘二,以此类推,再相加即可。所以,先获取k
在1-ans里的位置,再对1-ans进行二分,得到k
的前一个数ans
、最后根据k
和ans
所在位置的差值,确定k
是第几位数,输出。
在最后输出的时候,通过num2str函数将ans
转换为字符串,用来获取某位数字。
总结:
算了算数据点发现这题暴力就能拿60分,模测上第四题拿个60分表示已经心满意足了www
用前缀和写一直wa,等过了这段ddl高峰期,我再来try againTAT
代码:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
long long q,k,l,r,mid,ans;
long long solve(long long x,bool op)
{
long long a=1,bb=0,b=0,n=0,d=0;
while(1)
{
a*=10; //两位数起
d++; //等差数列的差
if(x>a-1) //从最大的一位数开始判断
{
n=a-a/10; //等差数列的项数
bb+=(b+d)*n+n*(n-1)/2*d; //c+d为首项
b+=n*d; //e.g. 10-99共90个数 90*2
}
else //x是a位数
{
n=x-a/10+1; //有n个数
bb+=(b+d)*n+n*(n-1)/2*d;
b+=n*d;
break;
}
}
if(op)
return bb;
else
return b;
}
string num2str(long long i) //数值转字符串
{
stringstream ss;
ss<<i;
return ss.str();
}
int main()
{
cin>>q;
for(int i=1;i<=q;i++)
{
cin>>k;
l=0;r=1000000000;
while(l<=r)
{
mid=(l+r)/2;
if(solve(mid,1)<k) //最后一个比k小的数
{
ans=mid;
l=mid+1;
}
else
r=mid-1;
}
//k在1-ans+1之间
k=k-solve(ans,1); //去掉1,1 2,。。。,1-ans所有的
l=0;r=ans+1;
while(l<=r)
{
mid=(l+r)/2;
if(solve(mid,0)<k)
{
ans=mid;
l=mid+1;
}
else
r=mid-1;
}
//1,2,...,ans,ans+1(k在这个数里)
k=k-solve(ans,0);
ans++;
string s=num2str(ans);
cout<<s[k-1]<<endl;
}
return 0;
}