【XSY2691】中关村 卢卡斯定理 数位DP

题目描述

  在一个 k 维空间中,每个整点被黑白染色。对于一个坐标为(x1,x2,,xk)的点,他的颜色我们通过如下方式计算:

  • 如果存在一维坐标是 0 ,则颜色是黑色。
  • 如果这个点是(1,1,,1)(每一维都是 1 ),这个点的颜色是白色
  • 如果这个点的k个前驱(任取一维坐标减 1 )中的白点有奇数个,那么这个点的颜色就是白色,否则就是黑色

      给出一个k维超矩形,求这个矩形内的白点个数。

       k9,1liri1015

题解

  先把所有坐标 1

  然后DP。

  设 S=(x1,x2,,xk)

  设 fS 为一个坐标为 S 点的颜色(1为白色, 0 为黑色)。

  fS=fS1fS2fSk。其中 S1,S2,,Sk S k个前驱。

  这个表达式同样可以看成 fS=(ki=1fSi)mod2

  那么可以看出 fS 就是从 (0,0,,0) 走到 S 的方案数mod2,就是 (x1+x2++xkx1 x2  xk)mod2

  我们推广一下卢卡斯定理,就会发现 fS=1 当且仅当 x1,x2,,xk 之间两两and和为 0

  可以用数位DP计算这个东西。

  时间复杂度:O(3logr)

  我偷懒写了 O(4logr) 的做法。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<utility>
using namespace std;
typedef pair<int,int> pii;
int n,s;
pii a[110];
int f[110][110][110];
int xx[110];
int yy[110];
int m1,m2;
int d[110];
int gao(int x)
{
    return x?s/x:0x3fffffff;
}
int gao(int l,int r,int h)
{
    int &s=f[h][l][r];
    if(~s)
        return s;
    while(l<=r&&d[l]<=h)
        l++;
    while(l<=r&&d[r]<=h)
        r--;
    if(l>r)
        return s=0;
    int i;
    s=0x7fffffff;
    for(i=l;i<r;i++)
        s=min(s,gao(l,i,h)+gao(i+1,r,h));
    int hh=gao(xx[r]-xx[l]);
    if(hh<=yy[h])
        return s;
    int v=upper_bound(yy+1,yy+m2+1,hh)-yy-1;
    s=min(s,gao(l,r,v)+1);
    return s; 
}
void solve()
{
    scanf("%d%d",&n,&s);
    int i;
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].first,&a[i].second);
        xx[i]=a[i].first;
        yy[i]=a[i].second;
    }
    sort(xx+1,xx+n+1);
    sort(yy+1,yy+n+1);
    m1=unique(xx+1,xx+n+1)-xx-1;
    m2=unique(yy+1,yy+n+1)-yy-1;
    memset(f,-1,sizeof f);
    for(i=1;i<=m1;i++)
        d[i]=0;
    for(i=1;i<=n;i++)
    {
        a[i].first=lower_bound(xx+1,xx+m1+1,a[i].first)-xx;
        a[i].second=lower_bound(yy+1,yy+m2+1,a[i].second)-yy;
        d[a[i].first]=max(d[a[i].first],a[i].second);
    }
    int ans=gao(1,m1,0);
    printf("%d\n",ans);
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    #endif
    int t;
    scanf("%d",&t);
    while(t--)
        solve();
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值