[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;
}

 

posted @ 2019-10-12 12:17  Rorschach_XR  阅读(199)  评论(1编辑  收藏  举报
//雪花飘落效果