• [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] imes 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
    ",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
    ",ans[i]);
        return 0;
    }
    
  • 相关阅读:
    锚点anchorPoint
    核心动画2
    核心动画1
    CALayer()CoreAnimation 核心动画 简称 CA
    storyBoard
    本地通知UILocalNotification
    CoreImage 可以用滤镜来处理图片,比如修改饱和度,亮度,对比度等
    GCD(2)
    NSOperation(多线程)2
    给你个图片的网络地址url,如何获取该图片的尺寸
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11660720.html
Copyright © 2020-2023  润新知