[XSY 1496][Lucas定理+数位dp]雪中送温暖

一道不错的题。
首先我们将整个坐标系数变为以(1…1)为中心
考虑如何判断一个点的颜色
不妨设该点为 ( x 1 x 2 . . . x k ) (x_{1}x_{2}...x_{k})
其中,xi非0
M = i = 1 k x i M=\sum_{i=1}^{k}x_{i}
则它的颜色为 ( M x 1 ) ( M x 1 x 2 ) ( M x 1 x 2 x 3 ) . . . ( M x 1 x 2 . . . x k 1 x k ) m o d   2 \binom{M}{x_{1}}*\binom{M-x_{1}}{x_{2}}*\binom{M-x_{1}-x_{2}}{x_{3}}*...*\binom{M-x_{1}-x_{2}-...-x_{k-1}}{x_{k}}mod\ 2
奇为红,否则为绿
运用Lucas定理,我们知道该式子为奇的充要条件:
x 1 + . . . + x k x_{1}+...+x_{k} 在二进制的意义下没有进位
于是我们得到了这道题的转化问题:
如果我们先容斥,我们可以得到每一维的上界,记为:
n u m 1 n u m 2 . . . n u m k num_{1}num_{2}...num_{k}
我们需要求有多少种 y 1 y 2 . . . y k y_{1}y_{2}...y_{k}
满足 y 1 + y 2 + . . . + y k y_{1}+y_{2}+...+y_{k} 没有进位,且
1 i k , y i n u m i \forall{1\leqslant i\leqslant k},y_{i}\leqslant num_{i}
可以运用数位dp在 O ( 2 k k l o g 2 R ) O(2^kklog_{2}R) 的时间内完成
总的时间复杂度为: O ( k 2 2 k l o g 2 R ) O(k2^{2k}log_{2}R)
这都过了…

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int Mod=998244353;
int K;
ll l[20],r[20];
int f[70][600];
ll num[20];
bool c[20];
inline int calc(){
    memset(f,0,sizeof(f));
    for(int i=0;i<K;++i)
        if(num[i]<0)return 0;
    f[60][(1<<K)-1]=1;
    for(register int i=60;i>=1;--i)
        for(register int j=0;j<(1<<K);++j)
            if(f[i][j]){
                for(register int k=0;k<K;++k)
                    if((1<<k)&j)c[k]=((1ll<<(i-1))&num[k]);
                    else c[k]=true;
                //无1
                int flag=j;
                for(register int k=0;k<K;++k)
                    if(((1<<k)&j)&&c[k])flag^=(1<<k);
                f[i-1][flag]=(f[i-1][flag]+f[i][j])%Mod;
                //有1
                for(register int k=0;k<K;++k)
                    if(c[k]){
                        int now=flag;
                        if((1<<k)&j)now|=(1<<k);
                        f[i-1][now]=(f[i-1][now]+f[i][j])%Mod;
                    }
            }
    int Ans=0;
    for(register int i=0;i<(1<<K);++i)Ans=(Ans+f[0][i])%Mod;
    return Ans;

}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
    scanf("%d",&K);
    for(register int i=0;i<K;++i){
        scanf("%lld",&l[i]);
        l[i]--;
    }
    for(register int i=0;i<K;++i){
        scanf("%lld",&r[i]);
        r[i]--;
    }
    int ans=0;
    for(register int i=0;i<(1<<K);++i){
        int cnt=0;
        for(register int j=0;j<K;++j)
            if(i&(1<<j))num[j]=l[j]-1,cnt++;
            else num[j]=r[j];
        int tmp=calc();
        if(cnt&1)ans=(ans-tmp+Mod)%Mod;
        else ans=(ans+tmp)%Mod;
    }
    printf("%d\n",ans);
}
return 0;
}

猜你喜欢

转载自blog.csdn.net/ezoilearner/article/details/82966658