CSP-J/S2 2021 游记
占坑。
upd on 2021.10.25 16:14:先放一张在我自己动态里发过的聊天记录截图。
其他的次要信息有空再补,说不定直接就被咕咕了。
更一波题解。
CSP-J
A
看一眼数据范围就知道是数学题了。
首先假设我们带回去的
那么留给我们的
那么考虑如何加大收益,一个显然的想法是使得我们的收益
不难想到计算一个“差距”:
然后看一看,如果这个“差距”离咱们的上界
反之就是“差距”过大,那么就考虑带回去的越多越好就好啦
#include<cstdio>
int init(){
char c = getchar();
int x = 0, f = 1;
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = (x << 1) + (x << 3) + (c ^ 48);
return x * f;
}
void print(int x){
if (x < 0) x = -x, putchar('-');
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
int main(){
int n = init(), L = init(), R = init();
int x = (n - 1) - L % n;
if (L + x <= R)
print(n - 1), putchar('\n');
else
print(R % n), putchar('\n');
}
B
观察数据范围,发现给的
但是
所以说我们就可以得到:
每次操作
首先考虑操作
考虑桶排,如果我们把有贡献的数字离线下来,顶多不会超过
然后离散化一下可以缩小值域,至此我们每次操作
注意到这个有重复元素,所以再多记录一些信息就好了。
具体而言,需要知道每个数字在对应位置上是第几次出现,还是由于
详见代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define findb(x) std::lower_bound(b+1,b+1+len,x)-(b)
#define findc(x) std::lower_bound(c+1,c+1+c[0],x)-(c)
inline int in();
inline void wr(int);
const int N=(int)8e3+5,M=(int)5e3+5,K=(int)2e5+5;
int a[N],b[N+M],c[N];
struct Node{
int tp,x,v;
}s[K];
int tong[N+M],len,times[N+M],pos[N];
// times[i]: i 出现了几次
// pos[i]: a[i] 第几次出现
inline void translate();
int main(int argc,char**argv){
#ifndef ONLINE_JUDGE
freopen("7910.in","r",stdin);
freopen("7910.out","w",stdout);
#endif
register int n=in(),q=in();
for(register int i=1;i<=n;++i)
b[i]=a[i]=in();
len=n;
for(register int i=1;i<=q;++i){
s[i].tp=in();
if(s[i].tp==1){
s[i].x=in(),s[i].v=in();
b[++len]=s[i].v;
}
else
s[i].x=in();
}
std::stable_sort(b+1,b+1+len);
len=std::unique(b+1,b+1+len)-(b+1);
for(register int i=1;i<=n;++i)
a[i]=findb(a[i]);
for(register int i=1;i<=q;++i)
if(s[i].tp==1)
s[i].v=findb(s[i].v);
// 然后咋写……我想想。。。
for(register int i=1;i<=n;++i)
++tong[a[i]];
memset(times,0,sizeof(times));
for(register int i=1;i<=n;++i)
pos[i]=++times[a[i]];
translate();
for(register int i=1;i<=q;++i)
if(s[i].tp==2){
// 查询
register int id=findc(a[s[i].x]);
wr(id+pos[s[i].x]-1),putchar('\n');
// 1 2 3 4 5
// 5 5 5 5 5
// ^
}
else{
// 修改
--tong[a[s[i].x]];
a[s[i].x]=s[i].v;
++tong[a[s[i].x]];
memset(times,0,sizeof(times));
for(register int i=1;i<=n;++i)
pos[i]=++times[a[i]];
translate();
}
}
inline void translate(){
c[0]=0;
for(register int i=1;i<=len;++i){
register int x=tong[i];
while(x--)
c[++c[0]]=i;
}
}
inline int in(){
register char c=getchar();
register int x=0,f=1;
for(;c<'0'||c>'9';c=getchar())
if(c=='-')f=-1;
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c&15);
return x*f;
}
inline void wr(int x){
if(x<0)putchar('-'),x=-x;
if(x/10)wr(x/10);
putchar(x%10+'0');
}
C
小型模拟题。
感觉出题人挺良心,没有什么很大的坑点或者不注意就挂的地方。
这都写不对只能说明代码能力有待提升了。
首先观察可知,原题可以分为两个部分:
- 检查一个地址是否合法;
- 加入一个地址。
后者可以用 map 维护,是基础操作,所以关键点就落在了前者上。
首先是一波最基础的判断:
.出现 3 次,:出现 1 次。- 它们出现的顺序必须是
.、.、.、:。 - 没有别的什么奇怪字符(除
0,1,2,3,4,5,6,7,8,9,:,.之外)。
然后再模仿我们的快速读入写法,读入的时候注意判断一下前导零就可以了。
注意到本题数据范围,有可能某些写法需要开 long long,但是统计答案的时候动态检验,不合理即退出,这样就不是很困难,也不需要开 long long。
详见代码实现部分吧。
#include<map>
#include<cstdio>
#include<string>
#include<iostream>
const int N=(int)1e3+5;
std::map<std::string,int>map;
inline int in();
inline bool check(std::string);
int main(int argc,char**argv){
#ifndef ONLINE_JUDGE
freopen("7911.in","r",stdin);
freopen("7911.out","w",stdout);
#endif
std::ios::sync_with_stdio(false);
register int n=in();
for(register int i=1;i<=n;++i){
std::string op,ad;
std::cin>>op>>ad;
if(!check(ad))std::cout<<"ERR\n";
else if(op[0]=='S'){
if(map.count(ad))
std::cout<<"FAIL\n";
else{
map[ad]=i;
std::cout<<"OK\n";
}
}
else{
if(map.count(ad)){
std::cout<<map[ad]<<std::endl;
}
else{
std::cout<<"FAIL\n";
}
}
}
}
inline bool check(std::string str){
register int cnt1=0,cnt2=0;
register int len=str.size();
for(register int i=0;i<len;++i){
if(str[i]=='.')
++cnt1;
if(str[i]==':')
++cnt2;
if(cnt2==1&&cnt1<3)return 0;
// 冒号必须出现在点之后
if((str[i]<'0'||str[i]>'9')&&(str[i]!='.')&&(str[i]!=':'))
return 0;
// 出现了我也不知道是啥的奇怪符号
}
if(cnt1!=3||cnt2!=1)return 0;
int num[5]={-1,-1,-1,-1,-1};
register int now=0;
str[len]='.';
for(register int i=0;i<len;++i){
if(num[now]==-1&&(str[i]<'0'||str[i]>'9'))
// 非数字
return 0;
else if(num[now]==-1&&(str[i]=='0'&&str[i+1]!='.'&&str[i+1]!=':'))
return 0; // 前导 0
else if(str[i]<'0'||str[i]>'9')
// 一个断点
++now;
else if(num[now]==-1)
num[now]=(str[i]&15);
else
num[now]=(num[now]<<1)+(num[now]<<3)+(str[i]&15);
// 普通数字读入
if(num[now]>65535)return 0;
}
register bool tp1=(0<=num[0]&&num[0]<=255);
register bool tp2=(0<=num[1]&&num[1]<=255);
register bool tp3=(0<=num[2]&&num[2]<=255);
register bool tp4=(0<=num[3]&&num[3]<=255);
register bool tp5=(0<=num[4]&&num[4]<=65535);
return tp1&&tp2&&tp3&&tp4&&tp5;
}
inline int in(){
register int x;
std::cin>>x;
return x;
}
D
听说还挺考细节,瞄了眼题还没来得及写,那就先咕着。反正链表模拟题应该不难大家都会。
总结
看来今年 CSP-J 又秉承了一次 A