3.24字节笔试题解
分享题解攒人品
(写题能力严重退化~
第一题
题意:凸多边形有n条边,每条边上ai个标记,问选三个标记组成三角形的方案数,对1e9+7取模(n<=1e5, ai <=1e9)
思路:dp
dp[i][j]表示前i条边取了j个标记,有转移式
dp[i][j+k] += dp[i-1][j] + C(ai, k) 第i条边取了k个标记。
由于j不超过3,k不超过2,且C(ai, k)可以直接乘除得到,复杂度O(n)
看到有大佬容斥思路做的,很妙,没想到哈哈哈。
第二题
题意: 给定一个字符串,求包含"byte"或者"dance"的子串数量(字符串长度不超过1e5)
思路:数数
先求出所有byte,dance子串的位置,我们记为一个区间[l,r],我们知道既然byte是个合法串,那么[l,r]区间往左往右延伸都是合法的。
然后就是怎么不重复计数了。我们对区间[l,r]按照l排序,从左往右扫,每个区间贡献是(l - last_l) * (n -1 - r + 1),其中last_l表示上个区间的左端点,n表示字符串总长。只要保持当前区间的左端点,不超过上个区间左端点就不会重复计数。复杂度O(n)
第三题
题意: 给n个二元组,每个二元组<u,v>表示v个u,比如<1,3>表示1 1 1。然后q次询问,每次问区间[l, r]和。(n<=1e5,u,v<=1e9)
思路:前缀和,二分,数数
对u和v分别做前缀和,u的前缀和sum数组,v的前缀和cnt数组。
对于询问[l,r]我们先在cnt数组中用lower_bound或者二分找到对应的下标pl和pr
答案则为sum[pr-1] - sum[pl] + (cnt[pl] - l + 1) * 对应的u + (r - cnt[pr-1] ) * 对应的u,复杂度O(n + qlogn)
怎么理解呢,把[pl,pr]拆成[pl+1, pr - 1] 还有pl和pr单独两个点算贡献。[pl+1, pr - 1]区间肯定是吃满每个数和数量的贡献,这里用前缀和算下就可以。pl和pr单独两个点就要看多出或者缺少多少个数组,然后乘上对应的u,这里说的有点抽象。。。
贴个代码吧
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int mod = 1e9 + 7;
const int N = 1e5 + 10;;
int n, q;
int sum[N], cnt[N];
array<int, 2> E[N];
signed main() {
cin >>n;
for(int i = 1; i <=n; i++){
int u, v;cin >> u >> v;
E[i] = {u, v};
cnt[i] = (cnt[i-1] + v);
sum[i] = (sum[i-1] + u * v % mod) % mod;
}
cin >> q;
while(q--){
int l, r;
cin >> l >> r;
int pl = lower_bound(cnt+1, cnt+1+n, l) - cnt;
int pr = lower_bound(cnt+1, cnt+1+n, r) - cnt;
int ans = (pr - 1 >= pl + 1 ? (sum[pr-1] - sum[pl] + mod) % mod : 0);
if(pl==pr){
ans += (r - l + 1) * E[pl][0] % mod;
ans %= mod;
}else {
ans += (cnt[pl] - l + 1 + mod) % mod * E[pl][0] % mod;
ans %= mod;
ans += (r - cnt[pr-1] + mod) * E[pr][0] % mod;
ans %= mod;
}
cout << ans << endl;
}
}
第四题
题意: 给01矩阵,行不超过5,列不超过1000。其中有些位置是问号'?'。现在要把'?'变成01,不能出现相邻的1,问合法方案数,对1e9+7取模。
思路:dp,状压
由于就5行,2^5也就32。
dp很容易想dp[i][s],表示第i列的字符串状态为s,s即二进制状压。
if(i列和i-1列没有相邻1) , 有转移dp[i][s] += dp[i-1][k],复杂度O(4 ^n * m)
代码是难写点。凭回忆写了下大概。
这里状压有个特殊之处,就是01已知的地方就用已知的数表示即可,比如某列式01?(我把列横过来),的状态压缩是010或者011。所以下面我在check函数中首先会去check这个状态的合法性(代码11到14行)。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int mod = 1e9 + 7;
const int N = 1e5 + 10;;
int n, m;
int dp[1010][31];
string s[10];
bool check(int col, int col_state, int last_col_state){
for(int i = 0; i < n; i++){
if(s[i][col] != '?' && ((col_state>>i&1) != s[i][col] - '0')){
return false;
}
}
for(int i = 0; i < n; i++){
int k = s[i][col] == '?' ? col_state>>i&1 : s[i][col] - '0';
if(k){
// 与上一行进行check
int last_col_k = col_state>>(i-1)&1;
if(last_col_k){
return false;
}
}
}
// 与前一列check
// 第一列无需与上一列check
if(col){
for(int i = 0; i < n; i++){
int k = s[i][col] == '?' ? col_state>>i&1 : s[i][col] - '0';
int last_col_k = last_col_state>>i&1;
if(k == 1 && k == last_col_k){
return false;
}
}
}
return true;
}
signed main() {
cin >> n >> m;
for(int i = 0; i < n; i++){
cin >> s[i];
}
for(int i = 0; i < m; i++){
for(int j = 0; j < (1<<n); j++){
if(i){
for(int k = 0; k < (1<<n); k++){
if(dp[i - 1][k] && check(i, j, k)){
dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod;
}
}
}
else {
if(check(i, j, -1)){
dp[i][j]++;
}
}
}
}
int ans = 0;
for(int i = 0; i < (1<<n) ; i++){
ans = (ans + dp[m-1][i])%mod;
}
cout << ans;
}
查看2道真题和解析