codeforces round 895 (div. 3)
E
题意:
给你一个整数数组a, 以及一个二进制字符串s, 现在有三种操作:
- 对于给定的区间[l, r],你需要反转s[l, r];
- 输出所有s[i] = 1的a[i]异或值
- 输出所有s[i] = 0的a[i]异或值
思路 1:
暴力做法是O(q * n)超时,想到使用差分来简化操作1,但最坏的情况下复杂度依然是O(q * n);
需要用到一个性质,想要反转二进制区间[l, r],只需要异或上这个区间,比如 [1010] ^ [1010] = [0101]
所以对于操作1,只需要求一遍异或前缀和,然后O(1)解决,操作2,3也类似:如果已知一段区间[l, r]的操作2的异或值为x, 那么 x ^= s[l - 1] ^ s[r]等价于将这段区间里s[i] = 1的值删除然后加上s[i] = 0
因为两个相同的数异或等于0,就相当于把它从区间删除
思路 2:
利用线段树.....
inline void solve()
{
int n; cin >> n;
std::vector<int> a(n + 1);
std::vector<int> sum(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum[i] = sum[i - 1] ^ a[i];
}
string s; cin >> s;
int ans = 0; // 所有1的异或值
for (int i = 1; i <= n; i++)
{
if (s[i - 1] == '1') ans ^= a[i];
}
int q; cin >> q;
while (q--)
{
int op; cin >> op;
if (op == 1)
{
int l, r; cin >> l >> r;
ans ^= sum[l - 1] ^ sum[r];
}
else
{
int g; cin >> g;
if (g == 1) cout << ans << ' ';
else cout << (ans ^ sum[n]) << ' ';
}
}
cout << endl;
}
F:
题意:
你有n个小动物, 每个小动物都有一个售价和一个害怕的动物,a[i]表示 i 害怕 a[i];
- 如果 i 在 a[i] 之前卖出那么你可以得到 2 * c[i] 的报酬
- 反之你可以获得 c[i] 的报酬
请确定一个售卖顺序使得获得报酬最多
思路:
可以把害怕关系连成个图(i, a[i])连有向一条边,为了获得最多的报酬,应该按照topsort的顺序卖,但是图可能存在环但是topsort解决不了环的问题,所以必须把环拆开
优先把售价最小的那条边消掉,这样的话报酬就能最大
inline void solve()
{
int n; cin >> n;
std::vector<int> a(n + 1);
std::vector<int> c(n + 1);
std::vector<int> d(n + 1);
std::vector<bool> st(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
d[a[i]]++;
}
std::vector<int> ans;
for (int i = 1; i <= n; i++)
cin >> c[i];
// 先按topsort卖
queue<int> q;
for (int i = 1; i <= n; i++)
{
if (d[i] == 0) q.push(i);
}
while (!q.empty())
{
int t = q.front();
q.pop();
ans.pb(t);
st[t] = true;
if (--d[a[t]] == 0) q.push(a[t]);
}
for (int i = 1; i <= n; i++)
{
if (!st[i])
{
int idx1 = a[i];
int idx = i;
while (idx != idx1) // 找到环里面售价最小的那个
{
if (c[idx1] < c[idx]) idx = idx1;
idx1 = a[idx1];
}
int k = a[idx]; // 从最小值的下一个开始找,就能让最小值最后卖出
while (!st[k])
{
ans.pb(k);
st[k] = true;
k = a[k];
}
}
}
for (int i = 0; i < n; i++)
cout << ans[i] << " \n"[i == n - 1];
}
G
题意:
已知一个正整数序列a, 你需要选择一个区间[l, r]将这段区间替换成区间的乘积,要求替换后序列和最大
思路:
对于乘积来说选择a[i] = 1是不划算的因为不仅不会增加乘积还会浪费一个1,所以1
肯定不能作为端点,所以直观来说可以选择第一个非1点,和最后一个非1点,作为端点
但是也可能中间有1,所以需要判断一下乘积是不是大于和,最坏的情况下大于2e14就可以了,如果不行的话就暴力O(n * n)选择端点,大于1的个数最多有50(
inline void solve()
{
int n; cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
int l = 1, r = n;
while (a[l] == 1 && l <= r) l++;
while (a[r] == 1 && r >= l) r--;
if (l > r) // 没有1
{
cout << "1 1" << endl;
return;
}
__int128 res = 1;
bool flag = false;
for (int i = l; i <= r; i++)
{
if (res * (__int128)a[i] > 2e14)
{
flag = true;
break;
}
res *= (__int128)a[i];
}
if (flag)
{
cout << l << ' ' << r << endl;
return;
}
std::vector<LL> s1(n + 1), s2(n + 1);
std::vector<int> p;
s2[0] = 1;
for (int i = 1; i <= n; i++)
{
if (a[i] != 1) p.pb(i);
s1[i] = s1[i - 1] + a[i];
s2[i] = s2[i - 1] * a[i];
}
LL ans = accumulate(a.begin(), a.end(), (LL)0);
PII op = make_pair(1, 1);
for (int i = 0; i < p.size(); i++)
{
for (int j = i; j < p.size(); j++)
{
int l = p[i], r = p[j];
LL t = s2[r] / s2[l - 1] + s1[n] - (s1[r] - s1[l - 1]);
if (t > ans)
{
ans = t;
op = make_pair(l, r);
}
}
}
cout << op.first << ' ' << op.second << endl;
}
本文作者:自动机
本文链接:https://www.cnblogs.com/monituihuo/articles/17693866.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步