NOIP2024 前集训:MX 炼石计划 NOIP 模拟赛 23
前言
music
《若思念便思念》
客家方言:
“月光 出来哩哦”
剪不断的时间
故事里有牵绊
豫州夜渐远
人在 丹霞山南
响起那时方言
谁又在谁心间
在北边
只等一只客家船
龙门山 听到雁归南
故楼圆 北望有炊烟
我在你心间
是你我执意相守的诺言
云开 我知道你在眷恋
眷恋再不会走远
即使相隔千里也并不遥远
风起 你知道我在期盼
期盼你回到身边
人若思念便思念 终会相见
客家童谣:
“月光光 秀才郎
骑白马 过莲塘
莲塘背 种韭菜
韭菜花 结亲家”
曲未散 萦绕山海间
心尤在 弹指间聚散
你在我心间
是你我执意相守的诺言
云开 我知道你在眷恋
眷恋再不会走远
即使相隔千里也并不遥远
风起 你知道我在期盼
期盼你回到身边
人若思念便思念 终会相见
啊
飞鸟 看故事里的过往
我和你穿越时光
时光里旧模样 是彼此惆怅
月光 把粼粼水面点亮
点亮挚爱的方向
人若思念便思念 终会相见
人若思念便思念 终会相见
刚才 huge 进来说明天模拟赛只能用 window 打,要是没有虚拟机就……(简要概括就是忍着),受不了了真要模拟考场环境的话那能不能把磁盘保护给我关了……
说实话现在让我去用 DEV 真受不了。
T1 和大多数人一样直接想到线段树,然后就打了,然后就被卡常了,赛后发现我线段树就是当数组用的,我是脑瘫……
赛时把 T1 所有数据全发下来了,\(200\) 多 M 根本下不来,教练给传到 ftp 上才下下来的,用了 CuFeO4 的 checker 把所有数据全跑一遍都过了,于是心满意足的交了。
但是那一版没有测时间,当时找了个自以为挺大的点跑了没 T 就交了,结果还有更大的点,直接死了 ̄へ ̄。
赛后 CuFeO4 告诉我他本来更新了 checker 有测时间了,但是要告诉我的时候我电脑正好死机了……
现在想起来了那一上午电脑死了三次机,但第一次救回来了,当时就开个大样例,眼睁睁看着系统监视器的内存从 \(30\%\) 直接干到 \(98\%\) 然后屏幕开始抽搐,还好手快点注销了,不然死透了。
T3 读错题了,丫的还有小数,然后电脑恰好死机了,没良心啊!!!而对于整数部分我赛时并不会快速求出 \(\sum\limits_{i=1}^{n}i^k\) 然后也没有做出来。
T1 排序
发现就是对于每一组 \((a_i,a_{i+1})\) 找到二进制下最高的不相等的一位,从而计算出这一位必须是 \(0/1\),如果存在矛盾的就是无解,否则必须是 \(1\) 的就是 \(1\),剩下都是 \(0\) 即可。
对于修改只改两组,直接线段树就做完了,但是发现这个线段树完全是当数组用的,所以改成桶就过了。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
#define fi first
#define se second
using namespace std;
const int N=1<<20;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar_unlocked();
for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,a[N],cnt[35][2]; pair<int,bool>b[N];
inline pair<int,bool> solve(int x,int y)
{
for(int i=30,tmp1,tmp2;~i;i--) if((tmp1=(x>>i)&1)!=(tmp2=(y>>i)&1))
return {i,tmp1}; return {30,0};
}
inline int ask()
{
int res=0; for(int i=30;~i;res|=(!!cnt[i][1])*(1<<i),i--)
if(cnt[i][0]&&cnt[i][1]) return -1; return res;
}
signed main()
{
freopen("sort.in","r",stdin),freopen("sort.out","w",stdout);
read(n); for(int i=1;i<=n;i++) read(a[i]);
for(int i=2;i<=n;i++) b[i]=solve(a[i-1],a[i]),cnt[b[i].fi][b[i].se]++;
for(write(ask()),puts(""),read(m);m;write(ask()),puts(""),m--)
{
int x,y; read(x,y),a[x]=y;
if(x>1) cnt[b[x].fi][b[x].se]--,b[x]=solve(a[x-1],y),cnt[b[x].fi][b[x].se]++;
if((++x)<=n) cnt[b[x].fi][b[x].se]--,b[x]=solve(y,a[x]),cnt[b[x].fi][b[x].se]++;
}
}
T2 交换
是道置换的板子,但是我之前并不会置换。
考虑 \(n\mid m\) 的情况,视每个 \(m\) 为一轮,那么每一轮都是一样的,因为置换有结合律,所以直接快速幂即可。
考虑一般情况,发现每一次操作后将其左移 \(m\bmod n\) 位又对其了,于是又可以继续跑下一轮,最后再右移回来即可,因为这两个玩意都满足结合律,所以还是直快速幂即可。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e5+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar_unlocked();
for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,a[N],b[N],c[N]; ll t,k;
struct aa
{
int a[N]; aa(){for(int i=0;i<n;i++) a[i]=i;}
inline aa operator * (const aa x) const
{aa res; for(int i=0;i<n;i++) res.a[i]=a[x.a[i]]; return res;}
}ans,pos,le,ri;
inline aa qpow(aa a,ll b)
{aa res; for(;b;a=a*a,b>>=1) if(b&1) res=res*a; return res;}
signed main()
{
freopen("swap.in","r",stdin),freopen("swap.out","w",stdout);
read(n,m,t),k=t/m;
for(int i=0;i<n;i++) read(a[i]),pos.a[i]=ans.a[i]=i;
for(int i=0;i<m;i++) read(b[i],c[i]);
for(int i=1;i<=m;i++) swap(pos.a[(b[i%m]+i)%n],pos.a[(c[i%m]+i)%n]);
for(int i=0;i<n;i++) le.a[i]=(i+m)%n,ri.a[i]=(i-m%n+n)%n;
if(k>0) ans=ans*pos,pos=le*pos,ans=ans*qpow(pos,k-1),ans=ans*qpow(ri,k-1);
for(int i=1;i<=(t%m);i++) swap(ans.a[(b[i]+i+k*m)%n],ans.a[(c[i]+i+k*m)%n]);
for(int i=0;i<n;i++) write(a[ans.a[i]]),putchar_unlocked(' ');
}
T3 计算
之前做过类似的需要二项式定理的期望 DP,所以这一部分就不说了。
考虑拆成整数和小数部分来处理,整数部分就是正常 DP,\(\sum\limits_{i=1}^{n}i^k\) 可以直接用拉格朗日插值求(刚学的)。
小数部分考虑 DP 求方案数,设 \(x_i\in[0,1)\),发现 \(\lfloor\sum\limits_{j=1}^{i}x_j\rfloor=\lfloor\sum\limits_{j=1}^{i-1}x_j\rfloor+1\) 当且仅当 \(\lfloor\sum\limits_{j=1}^{i}x_j\rfloor\) 的小数部分 \(<\lfloor\sum\limits_{j=1}^{i-1}x_j\rfloor+1\) 的小数部分,不妨设 \(s_i\) 表示 \(\lfloor\sum\limits_{j=1}^{i}x_j\rfloor\) 的小数部分,有 \(s_i\in[0,1)\),问题转化为求 \(\sum\limits_{i=2}^n[s_i<s_{i-1}]\),假设从小到大插,设 \(g_{i,j}\) 表示插入了 \(i\) 个数,有 \(\sum\limits_{k=2}^i[s_k<s_{k-1}]=j\) 时的方案数,那么有转移:
- 插在开头:\(g_{i,j}=g_{i-1,j-1}\)。
- 插在结尾:\(g_{i,j}=g_{i-1,j-1}\)。
- 插在一组 \(s_k<s_{k-1}\) 的中间,\(g_{i,j}=j\times g_{i-1,j}\)。
- 插在一组 \(s_k>s_{k-1}\) 的中级,\(g_{i,j}=(i-1-j)\times g_{i-1,j-1}\)。
综上,有:\(g_{i,j}=(j+1)\times g_{i-1,j}+(i-j)\times g_{i-1,j-1}\),可以 \(O(n^2)\) 求出。
那么小数部分的 \(k\) 次幂的期望就是 \(\dfrac{\sum\limits_{i=0}^ni^k\times g_{n,i}}{n!}\),最后在用二项式定理把这部分和整数部分合并 起来即可。
复杂度 \(O(nk^3)\),拉插因为下标连续可以提前预处理,复杂度为 \(O(nk^2)\)。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e3+10,M=25,P=998244353;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar_unlocked();
for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,f[M],ans[M],pre[M],suf[M],fac[N],inv[N],a[M][M],c[M][M],g[2][N];
inline int mod(int x,int y) {return (x+=y)>=P?x-P:x;}
inline int qpow(ll a,int b)
{ll res=1; for(;b;(a*=a)%=P,b>>=1) (b&1)&&((res*=a)%=P); return res;}
inline void init(int n)
{for(int i=1;i<=n+2;i++) a[n][i]=mod(a[n][i-1],qpow(i,n));}
inline int calc(int x,int k)
{
if(x<0) return 0; if(x<=k+2) return a[k][x]; pre[0]=suf[k+3]=1;
for(int i=1;i<=k+2;i++) pre[i]=1ll*pre[i-1]*(x-i)%P;
for(int i=k+2;i;i--) suf[i]=1ll*suf[i+1]*(x-i)%P;
ll res=0; for(int i=1;i<=k+2;i++)
{
ll up=1ll*pre[i-1]*suf[i+1]%P,down=1ll*inv[i-1]*inv[k+2-i]%P;
res=mod(res,mod((((k+2-i)&1)?-1:1)*a[k][i]*up%P*down%P,P));
} return res;
}
inline void solve(int x[],int y[])
{
int res[M]; memset(res,0,sizeof(res)),res[0]=1;
for(int i=1;i<=m;i++) for(int j=0;j<=i;j++)
res[i]=mod(res[i],1ll*c[i][j]*x[i-j]%P*y[j]%P);
memcpy(x,res,sizeof(res));
}
signed main()
{
freopen("calc.in","r",stdin),freopen("calc.out","w",stdout);
read(n,m),f[0]=ans[0]=1; for(int i=0;i<=m;i++) init(i);
for(int i=fac[0]=1;i<=max(n,m+2);i++) fac[i]=1ll*fac[i-1]*i%P;
inv[max(n,m+2)]=qpow(fac[max(n,m+2)],P-2);
for(int i=max(n,m+2)-1;~i;i--) inv[i]=1ll*inv[i+1]*(i+1)%P;
for(int i=c[0][0]=1;i<=m;i++) for(int j=c[i][0]=1;j<=i;j++)
c[i][j]=mod(c[i-1][j],c[i-1][j-1]);
for(int i=1,l,r;i<=n;i++,solve(ans,f))
{
read(l,r); ll tmp=qpow(r-l,P-2); for(int j=1;j<=m;j++)
f[j]=1ll*mod(calc(r-1,j),P-calc(l-1,j))*tmp%P;
}
g[0][0]=1; for(int i=1;i<=n;i++) for(int j=0;j<=i;j++)
g[i&1][j]=mod(1ll*(j+1)*g[!(i&1)][j]%P,j?1ll*(i-j)*g[!(i&1)][j-1]%P:0);
memset(f,0,sizeof(f)); for(int i=0;i<=m;f[i]=1ll*f[i]*inv[n]%P,i++)
for(int j=0;j<=n;j++) f[i]=mod(f[i],1ll*qpow(j,i)*g[n&1][j]%P);
return solve(ans,f),write(ans[m]),0;
}
T4 莫队
和题目名称一点关系没有,是想改的,但是还没有时间,今晚上有时间就改,不然的话估计这辈子改不了这题了。