[CSP-S模拟测试69]题解
kuku
A.chess
首先考虑$m=n$的情况,中间没有限制,所以直接设$dp[i][j]$为考虑前$i$列,共放$j$枚棋子的方案数转移即可。刷表控制一下边界。
不难发现$i$列和$i+pn$列的的情况是一样的,所以沿用上面那个转移,
改为 $dp[i][j+k]=\sum dp[i-1][j] \times C_(n,k)^{(m-i)/n+1}$ 。预处理组合数次幂。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
int n,K;
ll m,dp[105][10005],C[105][105],g[105][105];
ll qpow(ll a,ll b)
{
ll res=1;a=a%mod;
while(b)
{
if(b&1)res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int main()
{
scanf("%d%lld%d",&n,&m,&K);
C[0][0]=1;
for(int i=1;i<=n;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)
(C[i][j]=C[i-1][j]+C[i-1][j-1])%=mod;
}
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
g[i][j]=qpow(C[n][i],(m-j)/n+1);
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=(i-1)*n;j++)
for(int k=0;k<=n;k++)
(dp[i][j+k]+=dp[i-1][j]*g[k][i]%mod)%=mod;
}
cout<<dp[n][K]<<endl;
return 0;
}
B.array
其实就是求某个位置到左侧第一个比它大的位置之间最小值的位置。单纯求左侧第一个大于该点的位置显然可以单调栈,那么对于这道题,只要多开一个数组$L[]$表示最小值位置,并在弹栈时用栈内元素更新该点的$L[]$就行了。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=1e7+2;
int n,a[N],L[N],ans;
stack<int> s;
int main()
{
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++)
{
L[i]=i;
while(!s.empty()&&a[s.top()]<=a[i])
{
if(a[L[s.top()]]<=a[L[i]])
L[i]=L[s.top()];
s.pop();
}
s.push(i);
ans=max(ans,i-L[i]+1);
}
printf("%d\n",ans);
return 0;
}
C.ants
题意:对于每次询问,求$l$和$r$之间最长连续值域段的长度。
信息难以直接用线段树维护和合并,考虑莫队。容易发现本题容易在左右指针移动时添加信息,而删除是比较困难的。所以就是回滚莫队的板子。
简单口胡一下(没准以后有时间会把根号算法放一起总结一下):
询问排序第一关键字左端点所在块,第二关键字右端点。
初始化:左指针位于该块右端点+1,右指针位于该块右端点。
如果询问的左右端点在一个块中,直接暴力。
否则,由于右端点升序,右侧进行的肯定都是添加操作。
那左侧呢?从原位置出发,只做添加操作,完毕后撤销影响回到该块右端点+1。
针对这道题,因为需要维护值域段长度,所以考虑并查集。显然路径压缩是无法撤销的,所以需要按秩合并。
一点细节:操作很多,并查集的合并次数更多,每次都要将之前的操作集合入栈,这样并不能保证每个元素只会在栈里出现一次,所以如果用数组模拟栈的话需要开很大。当然STL就不存在这个问题了。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<stack>
#include<algorithm>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=1e5+5;
const int inf=0x3f3f3f3f;
int n,m,a[N],bel[N],bl,ans[N];
int fa[N],size[N],mind[N],maxd[N],vis[N];
stack<int> s;
int findf(int x){return fa[x]?findf(fa[x]):x;}
struct query
{
int l,r,id;
}q[N];
bool cmp(const query &x,const query &y)
{
return (bel[x.l]==bel[y.l])?(x.r<y.r):(bel[x.l]<bel[y.l]);
}
void merge(int x,int y)
{
x=findf(x),y=findf(y);
if(x==y)return ;
if(size[x]>size[y])swap(x,y);
fa[x]=y;size[y]+=size[x];
s.push(x);
}
int main()
{
n=read();m=read();
bl=sqrt(n);
for(int i=1;i<=n;i++)
{
a[i]=read();
bel[i]=(i-1)/bl+1;
}
for(int i=1;i<=m;i++)
q[i].l=read(),q[i].r=read(),q[i].id=i;
sort(q+1,q+m+1,cmp);
for(int i=0;i<=n;i++)
maxd[i]=0,mind[i]=inf;
int l,r,res,old,last;
for(int i=1;i<=m;i++)
{
if(bel[q[i].l]!=bel[q[i-1].l])
{
last=bel[q[i].l]*bl+1;
l=last;r=l-1;
for(int i=1;i<=n;i++)
fa[i]=vis[i]=0,size[i]=1;
res=old=0;
}
if(bel[q[i].l]==bel[q[i].r])
{
for(int j=q[i].l;j<=q[i].r;j++)
{
maxd[a[j]]=max(maxd[a[j]+1],a[j]);
mind[a[j]]=min(mind[a[j]-1],a[j]);
mind[maxd[a[j]]]=mind[a[j]];
maxd[mind[a[j]]]=maxd[a[j]];
ans[q[i].id]=max(ans[q[i].id],maxd[a[j]]-mind[a[j]]+1);
}
for(int j=q[i].l;j<=q[i].r;j++)
mind[a[j]]=inf,maxd[a[j]]=0;
continue;
}
while(r<q[i].r)
{
vis[a[++r]]=1;
if(vis[a[r]+1])merge(a[r],a[r]+1);
if(vis[a[r]-1])merge(a[r],a[r]-1);
res=max(res,size[findf(a[r])]);
}
old=res;
int form=s.size();
while(l>q[i].l)
{
vis[a[--l]]=1;
if(vis[a[l]+1])merge(a[l],a[l]+1);
if(vis[a[l]-1])merge(a[l],a[l]-1);
res=max(res,size[findf(a[l])]);
}
ans[q[i].id]=res;
res=old;
while(l<last)vis[a[l++]]=0;
while(s.size()>form)
{
int x=s.top();s.pop();
size[fa[x]]-=size[x];
fa[x]=0;
}
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
兴许青竹早凋,碧梧已僵,人事本难防。

浙公网安备 33010602011771号