• CCPC 2020 长春 J


    题意:

    给定n,在x轴[0,n]范围内画半径不超过5的圆,圆心在x轴上,要求任意两个圆不相交(可以相切),且已有固定的k个圆,求满足条件的画法方案数。

    题解:

    先不管已存在的圆考虑从左往右dp,dp[i]表示以i为右边界的方案数,显然从dp[i-1]到dp[i]的转移只和以i为右边界的圆有关,而圆的直径最大只有10,因此只需要知道左边10个点的状态就可以判断是否可以画圆。如果一个点在已经存在的圆的中间(不包括边界),那么这个点就不能作为以i为右边界圆的左边界,存状态只需要记录这些点是否可以作为左边界,而显然点i-1是可以作为左边界的,因为之前最大的点就是i-1,实际上只要记录每个点左边的9个点的状态就够了。

    由于圆的半径有5种,因此以i为右边界画圆有(2^5)种情况,即每种状态有(2^5)种转移方式,状态数有(2^9*n)个,复杂度为(O(2^{14}*n))是可行的

    对于已有的圆,在枚举点i作为右边界时,只要判断i周围+-10格范围内的圆造成的影响即可:

    • 如果i在一个圆内部,则不能以这个圆外部的点作为左边界
    • 如果i在一个圆外部,则不能以这个圆内部的点作为左边界
    • 如果i在一个圆的右边界,则不能以这个圆的左边界作为左边界
    #include <bits/stdc++.h>
    using namespace std;
    const int mod=1e9+7;
    const int maxn=2e3+5;
    const int maxs=(1<<9)+1;
    const int b9=(1<<9)-1;
    int dp[maxn][maxs];
    int b[maxn][6];
    int nt[maxn];
    int p,pre,Left;
    inline int f(int n){//高位为1,低n位为0
        int ans=(1<<30)-1;
        int temp=(1<<n)-1;
        return ans^temp;
    }
    void dfs(int r,int mx){//r:枚举到的圆半径 mx:和已画的最大圆直径
        if(r==6){
            int temp=mx==0?Left:Left&f(mx-1);//加上新增加圆后的状态
            dp[p][temp&b9]+=dp[p-1][pre];
            dp[p][temp&b9]%=mod;
            return;
        }
        int d=r+r;
        if((p-d>=0)&&((1<<(d-1))&Left)&&(!nt[p-d]&&!nt[p])){
            dfs(r+1,d);
        }
        dfs(r+1,mx);
    }
    int main () {
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=k;i++){
            int c,r;
            scanf("%d%d",&c,&r);
            b[c+r][r]=1;
        }
        dp[0][0]=1;
        for(int i=1;i<=n;i++){
            for(int j=max(0,i-11);j<=i;j++) nt[j]=0;
            for(int j=max(0,i-11);j<=min(n,i+11);j++){
                for(int r=1;r<=5;r++){
                    if(b[j][r]){
                        if(i<=j-r-r)//border
                            continue;
                        else if(i==j){//border
                            nt[j-r-r]=1;
                        }
                        else if(j-r-r<i && i<j){//in
                            for(int k=max(0,i-11);k<j-r-r;k++){
                                nt[k]=1;
                            }
                        }
                        else{//out
                            for(int k=j-r-r+1;k<j;k++){
                                nt[k]=1;
                            }
                        }
                    }
                }
            }
            for(int st=0;st<(1<<9);st++){
                if(!dp[i-1][st])continue;
                p=i,pre=st,Left=st<<1|1;
                dfs(1,0);
            }
        }
        int ans=0;
        for(int st=0;st<(1<<9);st++){
            ans=(ans+dp[n][st])%mod;
        }
        printf("%d
    ",ans);
    }
    
  • 相关阅读:
    省市区(简版)
    利用 NSSortDescriptor 对 NSMutableArray 排序
    Objective-C 高性能的循环遍历 forin
    iOS-内存管理
    ios-遍历和排序
    寒假 OC-代理,类目,内存,协议,延展,数组,字典,集合
    Oracle SQL篇(二)oracle自连接操作
    Oracle SQL篇(三)Oracle ROWNUM 与TOP N分析
    Oracle SQL篇(四)group by 分组与分组的加强 rollup
    ODI中删除数据的处理
  • 原文地址:https://www.cnblogs.com/ucprer/p/13966169.html
Copyright © 2020-2023  润新知