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;
}