还是记录一下这场糟糕的比赛
F题
一个显然的结论是只需要管前i个位置最多选多少个红球和蓝球。
只要满足这个条件的序列一定是可以构造的。这个感性的猜一猜
再转化一下就是长度为i的前缀的球一定来自前i个,顺着更新一遍mx就好。
比赛的时候竟然觉得这个东西太简单,肯定是错的就没有写。求mx还想复杂了。需要make sure自己的结论!思维要更加清晰!
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<ll,ll> pr;
const ll mod = 998244353;
const int maxn = 4020;
char ch[maxn];
int mxr[maxn],mxb[maxn],r,b,n;
ll f[maxn][maxn];
inline void up(ll &x,ll y) { x = (x+y) % mod; }
int main(){
scanf("%s",ch + 1);
n = strlen(ch + 1);
rep(i,1,n){
if ( ch[i] == '0' ) r += 2;
else if ( ch[i] == '2' ) b += 2;
else r++ , b++;
mxr[i] = min(mxr[i - 1] + 1,r);
mxb[i] = min(mxb[i - 1] + 1,b);
}
rep(i,n + 1,n * 2){
mxr[i] = min(mxr[i - 1] + 1,r);
mxb[i] = min(mxb[i - 1] + 1,b);
}
f[0][0] = 1;
rep(i,0,n * 2){
rep(j,i - mxb[i],mxr[i]){
up(f[i + 1][j],f[i][j]);
up(f[i + 1][j + 1],f[i][j]);
}
}
cout<<f[n * 2][r]<<endl;
}
E题
一个0,1矩阵,选择一个子矩阵使得所有元素和为奇数
选择子矩阵是选择一个行和列的集合,他们交集的矩阵
n , m <= 100
比赛的时候竟然没有想出来。
%2意义的加法首先应该想到抑或。
一个很重要的结论是如果确定一个选行的集合,只要有一列使得这几行的位置上的数抑或和为1,方案数为2列数-1,因为选不选这一列的答案一定反转。
所以统计的是有多少行的集合,每一列抑或和都是0.
所以就是把行看成未知数,列看成方程。高消即可,方案数是2自由元,因为主元的状态随自由元唯一确定!
总结:
利用计数的性质,选和不选关键列的答案反转。
把行列的计数转化成只关于行的状态计数
这题如果想要DP或者容斥是无法表示状态的