9-17 模拟赛

· · 个人记录

四十多分钟阿克了,然后赢麻了。然后借着打模拟赛的名义开始看番(

A

小明最近在上化学课,他需要使用到 n 种化学物质来进行他的实验。在做实验的时候,他需要将所有化学物质放在桌面上,按次序排成一条直线。 然而每一种化学物质都是危险品,对于第 i 个化学物质,如果有另外一个化学物质距离它的距离小于 a_i,那么就会发生爆炸。 小明想知道如果要安全的完成他的实验,桌子最短可以多短。注意:物品要按原来的次序摆放。

非常 ez,答案显然是 \sum\limits_{i=2}^n \max\{a_i,a_{i-1}\}

#include <bits/stdc++.h>
#define rep(i, l, r) for(i=l; i<=r; ++i)
#define req(i, r, l) for(i=r; i>=l; --i)
using namespace std;
using ll=long long;
static const int Buf_size=1<<25;
static char _c; static int _x;
static const signed int base_10=10, zero(48), nine(57), flag_signed(45);
static char buf[Buf_size], *p1=buf, *p2=buf;
#define digit() (zero<=_c&&_c<=nine)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,Buf_size,stdin),p1==p2)?EOF:*p1++)
inline int read(){_x=0;_c=getchar();while(!digit()){_c=getchar();}while(digit()){_x=_x*10+(_c^zero);_c=getchar();}return _x;}
const int N=3e5+5, inf=2e9;
const ll illf=1e18;
int a[N];
int qt, n, i, j, k;
int main()
{
    n=read(); rep(i, 1, n) a[i]=read();
    ll res=0; rep(i, 2, n) res+=max(a[i-1], a[i]);
    printf("%lld", res);
}

B

小可可上初中了,初中真好啊,有很多自修课哦。很多同学喜欢在自修课时到教室外面去,说是到老师那问问题。 学校规定,自修课到教室外去的每个同学都必须做好登记,每次进出教室的登记是以一对整数 ab 来描述的,表示某一个同学在时刻 a 时到教室外面,在时刻 b 以后回到教室内。也就是说在时刻 a 至时刻 b 的这段时间中,这个登记的同学一直在教室外面。 校长想知道最多有多少同学在同一时刻都在教室外面,但同学们进进出出教室的记载实在很乱,于是校长请参加信息学兴趣小组的小可可来统计。

简单差分题。

#include <bits/stdc++.h>
#define rep(i, l, r) for(i=l; i<=r; ++i)
#define req(i, r, l) for(i=r; i>=l; --i)
using namespace std;
using ll=long long;
static const int Buf_size=1<<25;
static char _c; static int _x;
static const signed int base_10=10, zero(48), nine(57), flag_signed(45);
static char buf[Buf_size], *p1=buf, *p2=buf;
#define digit() (zero<=_c&&_c<=nine)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,Buf_size,stdin),p1==p2)?EOF:*p1++)
inline int read(){_x=0;_c=getchar();while(!digit()){_c=getchar();}while(digit()){_x=_x*10+(_c^zero);_c=getchar();}return _x;}
const int N=3e5+5, V=1e8, inf=2e9;
const ll illf=1e18;
int a, b, d[V+5];
int qt, n, i, j, k, res;
int main()
{
    n=read();
    rep(i, 1, n)
    {
        a=read(); b=read();
        ++d[a]; --d[b+1];
    }
    rep(i, 1, V) d[i]+=d[i-1];
    rep(i, 1, V) res=max(res, d[i]);
    printf("%d\n", res);
}

C

\max\limits_{r-l\ge k} \gcd(a_l, a_r)。是学长 1kri 出的题!

非常简单的题吧!你先记录对于 $k$ 他最早出现和最晚出现。 然后倒序枚举答案,暴力跳,求最早和最晚这个答案的倍数,其位置一减 $\ge k$ 就可以直接输出了。 复杂度就是一个调和级数部分和,那么就是 $V\log V$。 ```cpp #include <bits/stdc++.h> #define rep(i, l, r) for(i=l; i<=r; ++i) #define req(i, r, l) for(i=r; i>=l; --i) using namespace std; using ll=long long; static const int Buf_size=1<<25; static char _c; static int _x; static const signed int base_10=10, zero(48), nine(57), flag_signed(45); static char buf[Buf_size], *p1=buf, *p2=buf; #define digit() (zero<=_c&&_c<=nine) #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,Buf_size,stdin),p1==p2)?EOF:*p1++) inline int read(){_x=0;_c=getchar();while(!digit()){_c=getchar();}while(digit()){_x=_x*10+(_c^zero);_c=getchar();}return _x;} const int N=3e5+5, V=1e6, inf=2e9; const ll illf=1e18; int a[N], mx[V+5], mn[V+5]; int qt, n, i, j, k; int l, r; int main() { n=read(); k=read(); rep(i, 1, V) mx[i]=-inf, mn[i]=inf; rep(i, 1, n) a[i]=read(), mx[a[i]]=max(mx[a[i]], i), mn[a[i]]=min(mn[a[i]], i); req(i, V, 1) { l=1e6; r=-1e6; for(j=i; j<=V; j+=i) l=min(l, mn[j]), r=max(r, mx[j]); if(1ll*r-l>=k) {printf("%d\n", i); break;} } return 0; } ``` 同学 @Monomial 有一个很蠢的 $n\sqrt V$ 的做法,也是当时他们集训场上的首 A。 ```cpp void wk() { memset(l,-1,sizeof l); memset(r,-1,sizeof r); read(n),read(k); for(int i=1;i<=n;++i) { read(a[i]); for(int j=1;j*j<=a[i];++j) { if(a[i]%j==0) { if(l[j]==-1) l[j]=r[j]=i; else r[j]=i; if(l[a[i]/j]==-1) l[a[i]/j]=r[a[i]/j]=i; else r[a[i]/j]=i; } } } for(int i=1000000;i>=1;--i) { if(l[i]==-1) continue; if(r[i]-l[i]>=k) { writeln(i); return ; } } } ``` ## D 给定一个长为 $n$ 的 $01$ 字符串 $S$。求对长度为 $n$ 的字符串 $S$ 序列有多少种操作,满足 $A_0=S$,$A_n$ 为空,$A_i$ 长度为 n−i,且 $\forall 1\le i\le n$,$A_i$ 是 $A_{i−1}$ 的子序列。定义 $S$ 是 $T$ 的子序列,当且仅当可以从 $T$ 中删去一些字符使之变为 $S$。$n\le 400$。 依然是 1kri 学长出的题。 他们集训时候就做了这题,而我在家里自闭。 设 $f(l,r)$ 表示能够删完 $[l,r]$ 的方案数。 考虑我们在 $[l,r]$ 中钦定一下 $k\in[l,r]$ 使得 $s_k$ 是最后一个被删的。 这个时候有一个问题,就是连续一段相同的数,删哪个是本质相同的。 那我们再钦定一个删数顺序,也就是对于连续的一段,删哪个我们都归功于最后一个。 例如 $000$,下一步肯定是 $00$,无论如何我们都认为删的是第 $3$ 个 $0$。 那现在出现一个什么状况呢?也就是当 $k$ 作为最后一个删的,会和 $l-1,r+1$ 相邻。 如果出现连续一大段怎么办?如果 $s_k=s_{l-1}$ 无所谓,反正归功到 $s_k$ 了,如果 $s_k=s_{r+1}$,我们这个就不可以转移了,因为你这样把 $s_{r+1}$ 或者更远的某个的功劳给抢了。 所以转移的条件是 $r=n$ 或者 $s_k\not = s_{r+1}$。 如何转移呢?最后删 $k$,显然就有 $f(l,k-1)$ 和 $f(k+1, r)$ 这两个更小的状态。这 $r-l$ 个字符中你要看前 $k-i$ 个字符是在哪些时间上的位置删的,这显然是一个组合数 $\dbinom {r-l} {k-i}$。其实这里写 $r-l$ 选 $r-k$ 是一模一样的。 ```cpp #include <bits/stdc++.h> #define rep(i, l, r) for(i=l; i<=r; ++i) #define req(i, r, l) for(i=r; i>=l; --i) using namespace std; using ll=long long; static const int Buf_size=1<<25; static char _c; static int _x; static const signed int base_10=10, zero(48), nine(57), flag_signed(45); static char buf[Buf_size], *p1=buf, *p2=buf; #define digit() (zero<=_c&&_c<=nine) #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,Buf_size,stdin),p1==p2)?EOF:*p1++) inline int read(){_x=0;_c=getchar();while(!digit()){_c=getchar();}while(digit()){_x=_x*10+(_c^zero);_c=getchar();}return _x;} const int N=1005, inf=2e9, p=1e9+7; const ll illf=1e18; int a[N]; ll f[N][N], c[N][N]; char str[N]; int qt, n, i, j, k, l; int main() { scanf("%d %s", &n, str+1); f[1][0]=1; rep(i, 1, n) f[i][i]=i==n||str[i]!=str[i+1], f[i+1][i]=1; rep(i, 0, n) c[i][0]=1; rep(i, 1, n) rep(j, 1, i) c[i][j]=(c[i-1][j]+c[i-1][j-1])%p; rep(l, 2, n) rep(i, 1, n-l+1) { j=i+l-1; rep(k, i, j) if(j==n || str[k]!=str[j+1]) (f[i][j]+=f[i][k-1]*f[k+1][j]%p*c[j-i][j-k]%p)%=p; } printf("%lld\n", f[1][n]); } ``` ### 小结一下? 前两题只写了五分钟不到,如果普及组赛场上也这个速度那真的赢麻了。 第三题写了十分多钟吧,这个想明白还是很轻松解决的。 第四题当时只想了个大概?重新推的式子,花的时间稍微多了一些。 总之代码能力还是再练练,题还是多做。最后一年要有悲壮的感觉!