牛客 小白月赛90. D---小A的线段 dfs+差分数组优化
题解
看csdn题解顺便复习了一下差分数组的知识 全忘光了
实际上差分数组b[i]就是将每个a[i]拆解
a[i]是b[0] + …… + b[i]
他的实现就是通过输入时每次赋值时b[i] = a[i] - a[i - 1]
将b[i]实现为从a[i - 1]走向a[i]需要加上b[i]
举例 a[3] = a[2] + b[3] 同时 a[2] = a[1] + b[2] 所以 a[3] = b[1] + b[2] + b[3]
那么当我们对b[1]+1 那他会使得a[1]及之后的数都加上+1 因为每个a[i]里面都含有b[1]
所以有公式 b[l] += c; b[r + 1] -= c;
就可以将序列中a[l, r]之间的每个数都加上c
当我们要输出a[i]时 就可以对b[i]作前缀和来求a[i]
for (int i = 1; i <= n; i++)
{
a[i] = b[i] + a[i - 1]; //前缀和运算
printf("%d ", a[i]);
}
差分题解
好了 这个问题解决了
解决dfs
我们可以一条一条线段的搜
当搜到某条线段时 分成选择该线段和不选该线段两种方式
选择该线段就把该线段标记为true 反之为false (记得恢复现场)
当把所有线段都搜完了 我们就可以判断是否每个整数点都被覆盖了两次
当我们选择某一线段时 怎么通过线段的两个端点去让这一区间内的整数点的覆盖次数都加1呢
利用差分数组
这道题不用初始化差分数组 初始每个整数点的覆盖次数都是0 那差分数组所有值也都是0
上代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 20, mod = 998244353; //N是整数点区间上限 M是线段个数上限
int n, m;
long long ans; //ans是方案数
int l[M], r[M]; //存线段的左右节点坐标
bool st[M]; //记录该线段是否使用过
int b[N]; //差分数组
void dfs(int u)
{
if (u > m) //如果最后一条线段已经搜完
{
memset(b, 0, sizeof b);
for (int i = 1; i <= m; i ++ )
{
//找使用过的线段 将其区间内的整数点覆盖次数加1
if(st[i])
{
//cout << u << endl;
b[l[i]] ++ ;
b[r[i] + 1] -- ;
}
}
//找完之后使用过的线段内的整数点覆盖次数都确定了 开始搜是否每个点覆盖次数都至少为2次
for (int i = 1; i <= n; i ++ )
{
b[i] = b[i - 1] + b[i]; //求差分数组前缀和 求出来每个点的前缀和也就是每个点的覆盖次数
if (b[i] < 2) return; //只要有一个点不满足覆盖次数大于等于2 直接返回
}
ans = ans + 1; //进行到这里证明没有return 每个整数点都满足要求 方案数+1
ans %= mod;
return;
}
//当不选择该线段
st[u] = false;
dfs(u + 1);
//当选择该线段
st[u] = true;
dfs(u + 1);
st[u] = false; //恢复现场
}
void solve()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i ++ )
{
scanf("%d%d", &l[i], &r[i]);
}
dfs(1);
printf("%d\n", ans % mod);
return;
}
int main()
{
solve();
return 0;
}