多校A层冲刺NOIP2024模拟赛27终结篇
不知道是不是我打的最后一场模拟赛了,记录一下吧,总体来说还不错,虽然 \(T1\) 方案数求错爆零了,但 \(T3\) 场切了,暴力打满的话有265,希望 \(NOIP\) 时也可以不让自己遗憾吧。
——我们终会在星辰里相遇
A 【模板】分治FFT
考虑每加进来一个数的贡献 \(x_1*x_2+(x_1+x_2)*x_3+...=x_1*x_2+x_1*x_3+x_2*x_3+...\) 可以发现实际上不管怎么取都是每两个数相乘的和,所以直接一种方案的答案乘方案书即可,每次合并时少一堆,所以方案数是 $\sum \limits_{i=2}^{n} \dbinom{i}{2} $。
点击查看代码
#include<bits/stdc++.h>
#define int long long
const int mod=998244353;
const int maxn=1e5+10;
using namespace std;
int n,a[maxn],sum[maxn],ans,jc[maxn];
int qpow(int x,int y)
{
int ans=1;
while(y)
{
if(y&1) ans=ans*x%mod;
x=x*x%mod;
y>>=1;
}
return ans;
}
int c(int n,int m)
{
return jc[n]*qpow(jc[m],mod-2)%mod*qpow(jc[n-m],mod-2)%mod;
}
signed main()
{
freopen("fft.in","r",stdin);
freopen("fft.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
jc[0]=1;
for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=n;i>=1;i--) sum[i]=(sum[i+1]+a[i])%mod;
for(int i=1;i<=n;i++) ans=(ans+a[i]*sum[i+1]%mod)%mod;
int temp=c(n,2);
for(int i=1;i<=n-2;i++) temp=temp*c(n-i,2)%mod;
cout<<ans*temp%mod;;
return 0;
}
/*
2
100001 100002
*/
B 【模板】最近公共祖先
构造,但是我没仔细推,我觉得题解说的很好,所以直接放了
点击查看代码
#include<bits/stdc++.h>
const int maxn=3e5+10;
using namespace std;
int n,m,cnt,dep[maxn];
int head[maxn],to[maxn<<1],nxt[maxn<<1],tot;
vector<int> s[maxn],t[maxn];
bool vis[maxn];
struct lsx
{
int x,y,z;
}ans[maxn];
void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void addm(int x,int y)
{
add(x,y),add(y,x);
}
void lsx(int x)
{
vis[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(vis[y])
{
if(dep[y]<dep[x]) s[x].push_back(y);
}
else dep[y]=dep[x]+1,lsx(y);
}
while(t[x].size()>1)
{
int p=t[x].back();t[x].pop_back();
ans[++cnt]={p,x,t[x].back()};
t[x].pop_back();
}
while(t[x].size()&&s[x].size())
{
ans[++cnt]={t[x].back(),x,s[x].back()};
s[x].pop_back(),t[x].pop_back();
}
for(auto i:s[x]) t[i].push_back(x);
}
int main()
{
freopen("lca.in","r",stdin);
freopen("lca.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,x,y;i<=m;i++)
cin>>x>>y,addm(x,y);
for(int i=1;i<=n;i++) if(!vis[i]) lsx(i);
cout<<cnt<<'\n';
for(int i=1;i<=cnt;i++) cout<<ans[i].x<<" "<<ans[i].y<<" "<<ans[i].z<<'\n';
return 0;
}
/*
*/
C 【模板】普通平衡树
关于我题解没看懂这件事,考虑新加进来一个数的贡献,由于保证了互不相同,所以新加进来一个数它要么使序列更长,要么就是替换掉最后一个数使值域差更大,每次更改只和最后两个数的值有关,直接对每个序列维护最后两个数的值和答案即可,暴力是 \(O(nq)\) 的,考虑线段树优化这个过程,两个序列段合并时发现,答案只和两个序列段边界的两个值的关系有关,所以线段树直接维护每个序列段最前面的两个数的值,最后面的两个数的值,序列长度,序列答案即可,合并的时候直接分讨边界四个数的大小关系即可,需要特判序列长度小于等于2时的情况。
点击查看代码
#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
#define mid (l+r>>1)
const int maxn=3e5+10;
using namespace std;
int n,q;
struct lsx
{
int fi[3],en[3],size,ans,lazy;
}m[maxn<<2],mm;
void add(int id,int x)
{
if(!m[id].size)
{
m[id].size++;
m[id].fi[1]=m[id].en[1]=x;
m[id].ans=1;
}
else if(m[id].size==1)
{
m[id].size++;
m[id].fi[2]=m[id].en[2]=x;
m[id].ans=2;
}
else
{
if(m[id].en[1]>m[id].en[2])
{
if(x>m[id].en[2])
{
m[id].en[1]=m[id].en[2];
m[id].en[2]=x;
m[id].ans++;
}
else
{
if(m[id].size==2)m[id].fi[2]=x;
m[id].en[2]=x;
}
}
else
{
if(x<m[id].en[2])
{
m[id].en[1]=m[id].en[2];
m[id].en[2]=x;
m[id].ans++;
}
else
{
if(m[id].size==2)m[id].fi[2]=x;
m[id].en[2]=x;
}
}
m[id].size++;
}
}
void solve(int x,int id)
{
mm=m[x];
if(mm.size==1)
{
add(id,mm.fi[1]);
return ;
}
if(!m[id].size)
{
m[id].size=mm.size;
m[id].fi[1]=mm.fi[1];
m[id].fi[2]=mm.fi[2];
m[id].en[1]=mm.en[1];
m[id].en[2]=mm.en[2];
m[id].ans=mm.ans;
}
else if(m[id].size==1)
{
if(mm.fi[1]<mm.fi[2])
{
if(m[id].fi[1]<mm.fi[1])
{
mm.fi[1]=m[id].fi[1];
m[id].size=mm.size;
m[id].fi[1]=mm.fi[1];
m[id].fi[2]=mm.fi[2];
m[id].en[1]=mm.en[1];
m[id].en[2]=mm.en[2];
m[id].ans=mm.ans;
}
else
{
mm.size++,mm.ans++;
mm.fi[2]=mm.fi[1];
mm.fi[1]=m[id].fi[1];
m[id].size=mm.size;
m[id].fi[1]=mm.fi[1];
m[id].fi[2]=mm.fi[2];
m[id].en[1]=mm.en[1];
m[id].en[2]=mm.en[2];
m[id].ans=mm.ans;
}
}
else
{
if(m[id].fi[1]>mm.fi[1])
{
mm.fi[1]=m[id].fi[1];
m[id].size=mm.size;
m[id].fi[1]=mm.fi[1];
m[id].fi[2]=mm.fi[2];
m[id].en[1]=mm.en[1];
m[id].en[2]=mm.en[2];
m[id].ans=mm.ans;
}
else
{
mm.size++,mm.ans++;
mm.fi[2]=mm.fi[1];
mm.fi[1]=m[id].fi[1];
m[id].size=mm.size;
m[id].fi[1]=mm.fi[1];
m[id].fi[2]=mm.fi[2];
m[id].en[1]=mm.en[1];
m[id].en[2]=mm.en[2];
m[id].ans=mm.ans;
}
}
}
else
{
if(m[id].en[1]>m[id].en[2])
{
if(mm.fi[1]<mm.fi[2])
{
if(mm.size==2) mm.en[1]=min(mm.en[1],m[id].en[2]);
m[id].size+=mm.size-1;
m[id].ans+=mm.ans-1;
m[id].en[1]=mm.en[1],m[id].en[2]=mm.en[2];
}
else
{
if(m[id].en[2]<mm.fi[1])
{
m[id].size+=mm.size;
m[id].ans+=mm.ans;
m[id].en[1]=mm.en[1],m[id].en[2]=mm.en[2];
}
else
{
if(mm.size==2) mm.en[1]=m[id].en[1];
m[id].size+=mm.size-2;
m[id].ans+=mm.ans-2;
m[id].en[1]=mm.en[1],m[id].en[2]=mm.en[2];
}
}
}
else
{
if(mm.fi[1]>mm.fi[2])
{
if(mm.size==2) mm.en[1]=max(mm.en[1],m[id].en[2]);
m[id].size+=mm.size-1;
m[id].ans+=mm.ans-1;
m[id].en[1]=mm.en[1],m[id].en[2]=mm.en[2];
}
else
{
if(m[id].en[2]>mm.fi[1])
{
m[id].size+=mm.size;
m[id].ans+=mm.ans;
m[id].en[1]=mm.en[1],m[id].en[2]=mm.en[2];
}
else
{
if(mm.size==2) mm.en[1]=m[id].en[1];
m[id].size+=mm.size-2;
m[id].ans+=mm.ans-2;
m[id].en[1]=mm.en[1],m[id].en[2]=mm.en[2];
}
}
}
}
}
void down(int id)
{
if(!m[id].lazy) return ;
solve(id,lid);
solve(id,rid);
m[lid].lazy=m[rid].lazy=1;
m[id].lazy=m[id].size=m[id].ans=m[id].fi[1]=m[id].fi[2]=m[id].en[1]=m[id].en[2]=0;
}
void update(int id,int l,int r,int s,int t,int x)
{
if(l>=s&&r<=t)
{
m[id].lazy=1;
add(id,x);
return ;
}
down(id);
if(mid>=s) update(lid,l,mid,s,t,x);
if(mid<t) update(rid,mid+1,r,s,t,x);
}
int query(int id,int l,int r,int x)
{
// cout<<l<<" "<<r<<" "<<x<<endl;
if(l==r) return m[id].ans;
down(id);
return x<=mid?query(lid,l,mid,x):query(rid,mid+1,r,x);
}
int main()
{
freopen("odt.in","r",stdin);
freopen("odt.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>q;
while(q--)
{
int op,l,r,x;
cin>>op;
if(op==1)
{
cin>>l>>r>>x;
update(1,1,n,l,r,x);
}
else
{
cin>>x;
cout<<query(1,1,n,x)<<'\n';
}
}
return 0;
}
/*
10 4
1 6 6 697
1 4 8 824
1 6 7 455
2 7
*/
stars