• 2020CCPC长春站 J



    题意

    在一张图中画圆,需要保证

    1. 每个圆圆心在(x)轴上,且是一个整数点

    2. 每个圆圆内任意一点的(x)坐标均在([0,n])

    3. 每个圆的半径是个整数,且不超过(5)

    4. 每个圆与其他任意一个圆的交点最多只能有一个

    已知已经存在(k)个圆(保证初始图合法),求有多少种画法能够使得最后整张图满足上述条件

    (什么也不画也是一种画法)




    题解

    (本题还有状压dp/记忆化搜索等解法)

    由于每个圆圆心都是(x)轴上的整数点,且半径也是整数,故与(x)轴恒存在两个交点

    所以可以将某个圆表示成一条线段((x,r) ightarrow[x-r,x+r])

    那么限制也就变成了没有线段的顶点位于其余线段内

    使用(dp[l][r][0])表示如果不画线段([l,r])代表的圆,有多少种画法

    使用(dp[l][r][1])表示如果线段([l,r])代表的圆,有多少种画法

    通过已经存在的圆,再定义两个数组

    (used[l][r])表示原图中已经存在线段([l,r])代表的圆

    (nouse[l][r])表示不能再画线段([l,r])代表的圆


    对于每个已经存在的((x,r)),将其转化为线段([l,r]=[x-r,x+r])

    直接标记(used[l][r]=true)

    再对不能画的圆的左右端点进行枚举

    如果(Lin[0,l-1], Rin[l+1,r-1])或者(Lin[l+1,r-1], Rin[r+1,n])

    就表示线段([L,R])代表的圆与([l,r])代表的圆有两个交点,此时标记(nouse[L,R]=true)


    接下来考虑区间dp

    区间长度(len=r-l)从小((2))枚举到大((n)

    则对于(dp[l][r][0]),可以由左顶点在(l)的可行方案加上右顶点在(r)的可行方案转移得来,以保证所有圆与([l,r])这个大圆不冲突

    据区间dp的通用方法,其中一个方案数可以直接由长度(-1)直接转移得到(这里直接转移顶点在(l)的可行方案

    所以得到(dp[l][r][0]+=dp[l][r-1][0]+dp[l][r-1][1])

    此时对于顶点在(r)的可行方案,如果仍然用上述方法转移到(dp[l][r][0]),发现会在不画的情况下出现重复计数(即(dp[l][r-1][0])(dp[l+1][r][0])不互相独立)

    所以对于另一个方案数,需要通过进一步枚举计算来得到

    这里我们枚举存在一个圆,左端点为(m),右端点为(r),又因为题目中半径限制为(5),故枚举只需要进行至多(5)

    转移方案为(dp[l][r][0]+=dp[m][r][1] imes(dp[l][m][0]+dp[l][m][1]))

    对于(dp[l][r][1]),如果在线段([l,r])能够不冲突地放置一个圆,那么(dp[l][r][1]=dp[l][r][0])成立

    其后,如果原图中已经存在([l,r])的圆,那么(dp[l][r][0]=0),表示这个圆不能不放


    综上,转移方程为(以右端点开始枚举)

    [dp[l][r][0]=sum left { egin{aligned} dp&[l][r-1][0]\ dp&[l][r-1][1]\ dp&[m][r][1] imes(dp[l][m][0]+dp[l][m][1]) end{aligned} ight . \ dp[l][r][1]= left { egin{aligned} dp[l][r][0]&, r-lle 10 && (r-l) mod 2=0\ 0&, else end{aligned} ight . \ dp[l][r][0]= left { egin{aligned} dp[l][r][0]&, used[l][r]=false\ 0&, else end{aligned} ight . ]

    如果以左端点开始枚举,转移方程为

    [dp[l][r][0]=sum left { egin{aligned} dp&[l+1][r][0]\ dp&[l+1][r][1]\ dp&[l][m][1] imes(dp[m][r][0]+dp[m][r][1]) end{aligned} ight . \ dp[l][r][1]= left { egin{aligned} dp[l][r][0]&, r-lle 10 && (r-l) mod 2=0\ 0&, else end{aligned} ight . \ dp[l][r][0]= left { egin{aligned} dp[l][r][0]&, used[l][r]=false\ 0&, else end{aligned} ight . ]




    程序

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod=1e9+7;
    
    bool used[1050][1050];
    bool nouse[1050][1050];
    ll dp[1050][1050][2];
    
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
        
        int n,k,x,r;
        cin>>n>>k;
        for(int i=1;i<=k;i++)
        {
            cin>>x>>r;
            used[x-r][x+r]=true;
            for(int R=x-r+1;R<x+r;R++)
                for(int L=0;L<x-r;L++)
                    nouse[L][R]=true;
            for(int L=x-r+1;L<x+r;L++)
                for(int R=x+r+1;R<=n;R++)
                    nouse[L][R]=true;
        }
        
        for(int i=0;i<n;i++)
            dp[i][i+1][0]=1;
        
        for(int len=2;len<=n;len++)
        {
            for(int l=0,r=len;r<=n;l++,r++)
            {
                if(nouse[l][r])
                    continue;
                
                dp[l][r][0]=(dp[l+1][r][0]+dp[l+1][r][1])%mod;
                
                for(int m=l+2;m<=min(l+10,r);m+=2)
                    dp[l][r][0]=(dp[l][r][0]+dp[l][m][1]*(dp[m][r][0]+dp[m][r][1]))%mod;
                
                if((r-l)%2==0&&r-l<=10)
                    dp[l][r][1]=dp[l][r][0];
                
                if(used[l][r])
                    dp[l][r][0]=0;
            }
        }
        
        cout<<(dp[0][n][0]+dp[0][n][1])%mod<<'
    ';
        
        return 0;
    }
    
  • 相关阅读:
    JavaScript函数之美~
    如何用iframe标签以及Javascript制作时钟?
    Hammer.js
    leadJS初构建
    SQLite-创建、附加、分离数据库
    Javascript异步编程的4种方法
    JavaScript的数组实现队列与堆栈的方法
    使用PHP创建一个REST API(译)
    HTTP API响应数据规范整理
    CMD 模块定义规范
  • 原文地址:https://www.cnblogs.com/stelayuri/p/13954926.html
Copyright © 2020-2023  润新知