C
题意
求数组的总价值
其中g(l , r) 表示在[l, r]范围内,能分出的连续的不同的段的数量;
题目还提供每次询问单点修改,求每次询问修改后的总价值。
思路
O(n) 计算初始总价值,对于每次单点修改只需要查看i - 1 和 i + 1位置与x的关系O(1)解决
可以发现这两段是完全独立的
计算初始总价值,我们每次计算sum(k) 表示 g(k, n) + g(k + 1, n) + … + g(n , n)
可以发现如果当前i跟i + 1的数不相同 sum(i + 1) = sum(i) - n + i - 1;
如果相同只减1
对于修改我们先单独考虑i 和 i - 1位置,如果它们的相同性改变了,每个g(l , r)答案只会在他们都出现的时候改变1
代码实现
int n, m;
int a[N];
int b[N];
int check()
{
b[1] = 1;
for (int i = 2 ; i <= n ; i ++)
{
if (a[i] != a[i - 1])
b[i] = b[i - 1] + 1;
else b[i] = b[i - 1];
}
int res = 0;
for (int i = 1 ; i <= n ; i ++)
res += b[i];
int last = res;
for (int i = 2 ; i <= n ; i ++)
{
if (a[i] != a[i - 1])
{
res += last - n + i - 2;
last = last - n + i - 2;
}
else
{
res += last - 1;
last --;
}
}
return res;
}
void solve()
{
cin >> n >> m;
for (int i = 1 ; i <= n ; i ++)
cin >> a[i];
int res = check();
while (m --)
{
int i, x;
cin >> i >> x;
if (a[i] == x)
{
cout << res << endl;
continue;
}
if (x == a[i - 1] and a[i] != a[i - 1]) res -= (n - i + 1) * (i - 1);
if (x != a[i - 1] and a[i] == a[i - 1]) res += (n - i + 1) * (i - 1);
if (x == a[i + 1] and a[i] != a[i + 1]) res -= (n - i) * i;
if (x != a[i + 1] and a[i] == a[i + 1]) res += (n - i) * i;
cout << res << endl;
a[i] = x;
}
}
D
题意
给定数组长度,再给定m个关系,每个关系保证给定x, y, v,使得a[x] | a[y] = v。求该数组的最小字典序。
思路
贪心:放更多的0,那么初始情况我们先把所有的位都赋上1。
记录下所有的关系,首先处理所有的0,对于v的位上所有的0,我们直接分配0即可,相当于一个与运算。
在0的情况考虑完后,我们再考虑1的情况。对于每个数之后的数,如果其他跟它有关系的数全都有这一位1,那么我们就可以删掉这个位置的1
代码实现
int n, m;
cin >> n >> m;
while (m --)
{
int x, y, d;
cin >> x >> y >> d;
edge[x].push_back ({y, d});
edge[y].push_back ({x, d});
}
for (int i = 1 ; i <= n ; i ++)
{
a[i] = (1 << 30) - 1;
for (auto e : edge[i])
a[i] &= e.y;
if (edge[i].empty()) a[i] = 0;
}
for (int i = 1 ; i <= n ; i ++)
for (int j = 0; j < 30 ; j ++)
if (a[i] >> j & 1)
{
bool ok = false;
for (auto e : edge[i])
if (! (a[e.x] >> j & 1) or e.x == i) ok = 1;
if (!ok) a[i] -= (1 << j);
}
for (int i = 1 ; i <= n ; i ++)
cout << a[i] << ' ';
cout << endl;