2020CCPC长春站 J - Abstract Painting (区间dp)


题意

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

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

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

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

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

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

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




题解

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

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

所以可以将某个圆表示成一条线段\((x,r)\rightarrow[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\)

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

如果\(L\in[0,l-1],\ R\in[l+1,r-1]\)或者\(L\in[l+1,r-1],\ R\in[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]\times(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 \{ \begin{aligned} dp&[l][r-1][0]\\ dp&[l][r-1][1]\\ dp&[m][r][1]\times(dp[l][m][0]+dp[l][m][1]) \end{aligned} \right . \\ dp[l][r][1]= \left \{ \begin{aligned} dp[l][r][0]&,\ r-l\le 10\ \&\&\ (r-l)\ mod\ 2=0\\ 0&,\ else \end{aligned} \right . \\ dp[l][r][0]= \left \{ \begin{aligned} dp[l][r][0]&,\ used[l][r]=false\\ 0&,\ else \end{aligned} \right . \]

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

\[dp[l][r][0]=\sum \left \{ \begin{aligned} dp&[l+1][r][0]\\ dp&[l+1][r][1]\\ dp&[l][m][1]\times(dp[m][r][0]+dp[m][r][1]) \end{aligned} \right . \\ dp[l][r][1]= \left \{ \begin{aligned} dp[l][r][0]&,\ r-l\le 10\ \&\&\ (r-l)\ mod\ 2=0\\ 0&,\ else \end{aligned} \right . \\ dp[l][r][0]= \left \{ \begin{aligned} dp[l][r][0]&,\ used[l][r]=false\\ 0&,\ else \end{aligned} \right . \]




程序

#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<<'\n';
    
    return 0;
}
posted @ 2020-11-10 17:21  StelaYuri  阅读(254)  评论(0编辑  收藏  举报