2021.10.12 模拟赛题解
就 tm 彻底自闭了,现在已经堕落到 NOIP 模拟赛一题都 AC 不了的地步了吗/fn/fn/fn
T1
wtm 跟个 sb 一样,签到题想 1h CSP-S 玩个鬼哦
我们考察原图的最短路 DAG,那么显然对于 DAG 上每条边 \((u,v)\),必然有 \(d_v=(d_u+1)\bmod 3\),而根据原图是连通图可知除了源点之外都存在入边,因此我们考虑求出满足不存在任何一个与 \(x\) 相连的点 \(y\),满足 \(d_x=(d_y+1)\bmod 3\) 的 \(x\) 组成的集合,根据之前的推论可知这样的点是唯一的,就是我们选择的源点 \(s\),因此如果该集合大小不等于 \(1\) 答案就是 \(-1\),否则我们求出源点后 bfs 一遍求出每个点的距离即可检验我们选择的源点是否符合条件。
T2
大分类讨论屑题,我奶奶出的题都比这个好/bs
首先特判掉亿些些情况,譬如 \(s=t,b=0,b=1,s>t,a=0\),下面默认 \(b>1\)。
我们枚举进行了多少次 \(\times b\) 操作,设为 \(c\),那么显然 \(+a\) 操作是不影响 \(s\bmod a\) 的值的,因此进行 \(c\) 次操作后能够到达 \(a\),当且仅当 \(b^c·s\le t\) 且 \(b^c·s\equiv t\pmod{a}\),接下来考虑如何安排剩余 \(k=\dfrac{t-b^c·s}{a}\) 次 \(+a\) 操作,那么显然我们肯定会贪心地先将尽可能多的 \(+a\) 操作放在第一步,因为这样一次操作等价于进行 \(b^c\) 次 \(+a\) 操作,但是这样不一定操作得完,我们可能还要在第二步进行一些 \(+a\) 操作,以此类推下去。不难发现这有点类似于 \(a\) 进制数位和,具体来说,安排剩余 \(k\) 次 \(+a\) 最少需要 \(\sum\limits_{i=0}^{c-1}\lfloor\dfrac{k}{a^i}\rfloor\bmod a+\lfloor\dfrac{k}{a^c}\rfloor\) 次 \(+a\) 操作。这个 \(\mathcal O(\log n)\) 地计算即可,总复杂度 \(\mathcal O(\log^2n)\)。
T3
一道直接把我送走的思维题/ll/ll,全场 AC 人数最多的题目我咋就切不掉呢/dk/dk
首先最优策略肯定是先空着手走到某个位置,然后在返回的过程中购买礼品。那么如果假设停留的位置的下标分别为 \(i_1,i_2,\cdots,i_k(i_j<i_{j+1})\),可以推得最小体力值可表示为 \(2i_k+\sum\limits_{j=1}^ki_j·a_{i_j}\),不难发现通过这个式子,我们可以将每个商店视作一个体积为 \(a_i·i\),价值为 \(a_i\) 的背包。因此原问题可转化为背包问题。
于是我赛时就在那里硬着头皮优化背包,心态爆炸/ll
注意到一个性质:对于任意 \(i\),都有在 \(i\sim n\) 这些物品中,我们取出的物品的价值之和 \(\le\dfrac{e}{i}\),因为在带上这些物品之后我们还要向左移动 \(i\) 的距离,因此我们考虑交换定义域和值域并从右往左将所有物品加入背包,即,我们设 \(dp_i\) 表示当前取出的物品价值之和为 \(i\) 最少要花费多少的代价,这样在加入物品 \(i\) 时我们枚举上界只用设到 \(\dfrac{e}{i}\),这样总复杂度就是 \(\sum\limits_{i=1}^n\dfrac{e}{i}=e\ln n\),可以通过。
using namespace fastio;
const int MAXN=1e6;
const int MAXM=3e7;
int n,m,a[MAXN+5],dp[MAXM+5];
int main(){
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);
read(n);read(m);
for(int i=1;i<=n;i++) read(a[i]);
memset(dp,63,sizeof(dp));
for(int i=n;i;i--){
for(int j=m/i;j>=a[i];j--)
chkmin(dp[j],dp[j-a[i]]+1ll*i*a[i]);
chkmin(dp[a[i]],1ll*i*a[i]+2*i);
} int res=0;
for(int i=0;i<=MAXM;i++) if(dp[i]<=m) res=i;
printf("%d\n",res);
return 0;
}
T4
考虑分治(没想到啊没想到/kk,这究竟是谁的过错呢/dk),假设我们当前分治到 \([l,r]\),假设 \(mid=\lfloor\dfrac{l+r}{2}\rfloor\),那么我们不妨假设 \(x\in[l,mid],y\in[mid+1,r]\),考虑如何统计贡献。不难发现暴力计算答案的过程等价于一个将元素压入栈的过程,每次考察栈顶元素,如果栈顶元素等于待插入的元素那就弹出栈顶,否则直接在栈顶压入新元素,如果最终栈为空则字符串符合条件。不难发现这等价于,对于任意断点 \(i\),我们求出将 \(1\to i\) 中元素压入栈后得到的栈,以及 \(n\to i+1\) 中元素按顺序压入栈后得到的栈,那么这两个栈应相同。因此我们考虑以 \(mid\) 为断点,对于 \([l,mid]\) 中所有元素我们求出将每个位置 \(x\) 改为另一个字符后得到的栈,这个可以通过实时维护 \([l,x-1],[x+1,mid]\) 对应的栈后在栈上二分最长前缀后哈希实现,然后将其压入一个哈希表,对于后一半则用类似方法维护,然后在哈希表中查询即可。
总复杂度 \(n\log^2n\),瓶颈在于合并两个栈时在上二分。
const int MAXN=1e5;
const int MOD=1004535809;
const int BS=191;
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int n,pw[MAXN+5],ipw[MAXN+5];
char s[MAXN+5];ll res=0;
struct STK{
int stk[MAXN+5],hs[MAXN+5],hsi[MAXN+5],tp=0;
int op_stk[MAXN+5],op_tp=0;
STK(){tp=op_tp=0;}
void push(int x,int on=1){
if(on) op_stk[++op_tp]=x;
if(tp&&stk[tp]==x) --tp;
else{
stk[++tp]=x;
hs[tp]=(hs[tp-1]+1ll*x*pw[tp])%MOD;
hsi[tp]=(hsi[tp-1]+1ll*x*ipw[tp])%MOD;
}
}
void undo(){push(op_stk[op_tp--],0);}
int pre_hsh(int x){return hs[x];}
int suf_hsh(int x){return 1ll*(hs[tp]-hs[tp-x]+MOD)*ipw[tp-x]%MOD;}
} L,R;
void solve(int l,int r){
if(l==r) return;int mid=l+r>>1;
for(int i=l;i<=mid;i++) L.push(s[i]-'a');
solve(mid+1,r);
for(int i=mid;i>=l;i--) L.undo();
for(int i=r;i>mid;i--) R.push(s[i]-'a');
solve(l,mid);
for(int i=mid+1;i<=r;i++) R.undo();
for(int i=l;i<=mid;i++) L.push(s[i]-'a');
for(int i=r;i>mid;i--) R.push(s[i]-'a');
// printf("solve [%d,%d]\n",l,r);
STK tmp;
unordered_map<int,int> cnt[3][3];
for(int i=mid;i>=l;i--){
L.undo();
for(int j=0;j<3;j++) if(s[i]-'a'!=j){
tmp.push(j);
int l=1,r=min(L.tp,tmp.tp),p=0;
while(l<=r){
int mid=l+r>>1;
if(L.suf_hsh(mid)==tmp.suf_hsh(mid)) p=mid,l=mid+1;
else r=mid-1;
} int hsh=(L.hs[L.tp-p]+1ll*tmp.hsi[tmp.tp-p]*pw[tmp.tp-p+L.tp-p+1])%MOD;
cnt[j][s[i]-'a'][hsh]++;tmp.undo();
} tmp.push(s[i]-'a');
}
for(int i=l;i<=mid;i++) tmp.undo();
for(int i=mid+1;i<=r;i++){
R.undo();
for(int j=0;j<3;j++) if(s[i]-'a'!=j){
tmp.push(j);
int l=1,r=min(R.tp,tmp.tp),p=0;
while(l<=r){
int mid=l+r>>1;
if(R.suf_hsh(mid)==tmp.suf_hsh(mid)) p=mid,l=mid+1;
else r=mid-1;
} int hsh=(R.hs[R.tp-p]+1ll*tmp.hsi[tmp.tp-p]*pw[tmp.tp-p+R.tp-p+1])%MOD;
res+=cnt[s[i]-'a'][j][hsh];tmp.undo();
} tmp.push(s[i]-'a');
}
}
int main(){
freopen("swap.in","r",stdin);freopen("swap.out","w",stdout);
scanf("%s",s+1);n=strlen(s+1);
for(int i=(pw[0]=1);i<=MAXN;i++) pw[i]=1ll*pw[i-1]*BS%MOD;
int iv=qpow(BS,MOD-2);
for(int i=(ipw[0]=1);i<=MAXN;i++) ipw[i]=1ll*ipw[i-1]*iv%MOD;
solve(1,n);printf("%lld\n",res);
return 0;
}