loj3627 「2021 集训队互测」这是一道集训队胡策题

考虑按照每一行中 1 的个数进行排序,用 \(r(i)\) 表示第 \(i\) 行 1 的个数
枚举列中我们选了 \(i\) 个 1,那么对于\(r(x)<i\) 的,这一行一定填 0,对于\(r(x)>i\) 的,这一行一定填 1
所以我们只需要考虑行中 1 的个数恰好是 \(i\) 的这些行
对于 \(r(x)<i\) 的行,所有的 \(c_{x,j}=1\)\(j\) 列一定要选 1,设他们的并是 \(pre_i\)
对于 \(r(x)>i\) 的行,所有的 \(c_{x,j}=0\)\(j\) 列一定要选 0,设他们的并是 \(suf_i\)
显然要 \(pre_{i-1}\cap suf_{i+1}=\empty\)
接下来仅考虑 \(r(x)=i\) 的行的集合 \(S\),若 \(S\neq \empty\)
考虑钦定选一个行的集合 \(S\),如果这些行长得不一样,那么一定无解,因为会出现矛盾的限制,可以随便举一组例子手玩一下就可以得到这个结论
否则一定方案是 \(2^{|S|}\)
如果 \(S=\empty\),答案就是一个组合数 \(\binom{|U\oplus suf_i|-|pre_i|}{i-|pre_i|}\)
不难利用 bitset 实现。
复杂度为 \(O(n^2)\),瓶颈在于读入,后面部分复杂度为 \(O(\frac{n^2}{\omega})\)
代码现在是loj上最优解

#include <bits/stdc++.h>

using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

const int N=5005;
const int mod=998244353;

typedef long long ll;
typedef double db;

# define chkmax(a,b) a=max(a,b)
# define chkmin(a,b) a=min(a,b)
# define PII pair<int,int>
# define mkp make_pair

template<typename T> void read(T &x){
    x=0;int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

int n;
char s[N];
int fac[N],inv[N];
int poww[N];
ll ans;
bitset<N> S[N],pre[N],suf[N];
vector<int> T[N];

int Qpow(int base,int ind){
    int res=1;
    while(ind){
        if(ind&1)res=1ll*res*base%mod;
        base=1ll*base*base%mod;
        ind>>=1;
    }
    return res;
}

int inc(int x,int y){
    return x+y>=mod?x+y-mod:x+y;
}

int C(int n,int m){
    if(n<m||m<0)return 0;
    return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}

int main()
{
    # ifdef YuukiYumesaki
    freopen("testdata.in","r",stdin);
    freopen("test1.out","w",stdout);
    # endif
    read(n);
    Rep(i,1,n){
        scanf("%s",s+1);
        Rep(j,1,n)if(s[j]=='1')S[i].set(j);
        T[S[i].count()].push_back(i);
    }
    fac[0]=1;
    Rep(i,1,n)fac[i]=1ll*fac[i-1]*i%mod;
    inv[n]=Qpow(fac[n],mod-2);
    _Rep(i,n-1,0)inv[i]=1ll*inv[i+1]*(i+1)%mod;
    poww[0]=1;
    Rep(i,1,n)poww[i]=inc(poww[i-1],poww[i-1]);
    Rep(i,1,n){
        pre[i]=pre[i-1];
        for(auto v:T[i])pre[i]|=S[v];
    }
    Rep(i,1,n)suf[n+1].set(i);
    _Rep(i,n,1){
        suf[i]=suf[i+1];
        for(auto v:T[i])suf[i]&=S[v];
    }
    Rep(i,0,n)
        if((pre[i]|suf[i])==suf[i]){
            ans+=poww[T[i].size()]-1;
            int t1=pre[i].count(),t2=suf[i].count();
            ans+=C(t2-t1,i-t1);
        }
    printf("%lld\n",ans%mod);
    return 0;
}
posted @ 2022-04-30 16:36  YuukiYumesaki  阅读(500)  评论(0编辑  收藏  举报