But my words, like sil|

MessageBoxA

园龄:4年10个月粉丝:4关注:0

2024-05-27 15:34阅读: 21评论: 0推荐: 0

NOI模拟 开关

涉及知识点:DP,组合数学

题意

n (109) 盏灯,可以做 m (103) 次操作,每次操作表示为一个长度为 n01bbi1 表示该对灯 i 的开关状态反转,一种关灯方法即为 m两两不同01 串组成的无序集合。现在 n 盏灯中前 v 盏是亮的,其他是灭的,请问有多少种关灯方法能恰好 m 次操作关掉所有灯,答案模 998244353

思路

学到一个非常好用的计数技巧:我们发现要计数的是集合这个要求很烦,但我们可以先求出有序情况下的答案,然后再除以阶乘,类比 Cnx 要比 Anx 多除以一个 x!。对应到这道题,我们先求出有多少种不同的操作序列能关掉所有的灯,然后除以 m! 得到答案(有序序列 无序集合)。

还可以发现一件事,因为最开始前 v 盏灯亮着而最后要全熄灭,因此所有操作的 01 串按位异或之后一定为前 v 位为 1,其他为 0 的串。说明假设已经确定了前 m1 个操作,第 m​ 次操作也就确定了,由此为转移进行 DP:

fim=i 时的答案,

fi=A2ni1(i1)[2n(i2)]×fi2

A2ni1 为前 i1 个两两不同的操作有多少种组合,但我们第 i 次操作的 01 串有可能会和之前 i1 个操作重复,因此要减去重复的部分。前 i1 个串中有 1 个和第 i 个重复,由于两两不同,那么剩下 i2 个串是不和第 i 个重复的,对于每种不重复的 i2 个串的组合(共 fi2 种),重复的那个串只有可能是剩下的 [2n(i2)] 种,并且这个串的位置有可能是前 i1 个中的任意一个地方,因此需要重复的部分总共有 (i1)[2n(i2)]×fi2 种。

代码

代码中预处理了 x!C2nx,同时将 A2nx 化为了 x!C2nx

#include<bits/stdc++.h>
using namespace std;
#ifdef ONLINE_JUDGE
#define getchar __getchar
inline char __getchar(){
    static char ch[1<<20],*l,*r;
    return (l==r&&(r=(l=ch)+fread(ch,1,1<<20,stdin),l==r))?EOF:*l++;
}
#endif
template<class T>inline void rd(T &x){
    T res=0,f=1;
    char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while('0'<=ch && ch<='9'){res=res*10+ch-'0';ch=getchar();}
    x=res*f;
}
template<class T>inline void wt(T x,char endch='\0'){
    static char wtbuff[20];
    static int wtptr;
    if(x==0){
        putchar('0');
    }
    else{
        if(x<0){x=-x;putchar('-');}
        wtptr=0;
        while(x){wtbuff[wtptr++]=x%10+'0';x/=10;}
        while(wtptr--) putchar(wtbuff[wtptr]);
    }
    if(endch!='\0') putchar(endch);
}
typedef long long LL;
const LL P=19260817,MAXN=1005;
LL n,m,v,fact[MAXN],c[MAXN],n2n,f[MAXN];
inline LL qpow(LL x,LL a){
    LL res=1;
    x%=P;
    while(a){
        if(a&1) res=res*x%P;
        x=x*x%P;
        a>>=1;
    }
    return res;
}
inline void solve(){
    rd(n);rd(m);rd(v);
    
    n2n=qpow(2,n);
    c[0]=1;
    for(LL i=1;i<m;i++){
        c[i]=c[i-1]*((n2n-i+1+P)%P)%P*qpow(i,P-2)%P;
    }

    f[0]=(v==0);
    f[1]=1;
    for(int i=2;i<=m;i++){
        f[i]=(fact[i-1]*c[i-1]%P - (i-1)*( (n2n-i+2+P)%P )%P*f[i-2]%P +P)%P;
    }
    wt(f[m]*qpow(fact[m],P-2)%P,'\n');
}
int main(){
    fact[0]=1;
    fact[1]=1;
    for(LL i=2;i<MAXN;i++){
        fact[i]=fact[i-1]*i%P;
    }
    int t;
    rd(t);
    while(t--) solve();
    return 0;
}

本文作者:MessageBoxA

本文链接:https://www.cnblogs.com/SkyNet-PKN/p/18215666

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   MessageBoxA  阅读(21)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 evening Corn Wave
  2. 2 Группа крови Кино
  3. 3 The Sound Of Silence Simon & Garfunkel
  4. 4 dB doll YUE.STEVEN
Группа крови - Кино
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.