D:比赛的时候没开出来,主要是不会处理那个长度,结果看完题解就是直接将其分配给每一个a[i],看来是题做少了,没这意识,接下来就好办了,求新维护的一段区间的sum<=x,我们利用线段树动态开点就好了;
类似:P5459 [BJOI2016]回转寿司 的写法,可以做完这题在做这道,找点感觉
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 2e5 + 10;
struct node
{
int l, r;
int res;
}tree[maxn*100];
int root, tot, n, m;
int sum[maxn];
void insert(int&rt, int l, int r, int q)
{
if (rt == 0)
rt = ++tot;
tree[rt].res++;
if (l == r)
{
return;
}
int mid = l + r >> 1;
if (q <= mid)
{
insert(tree[rt].l, l, mid, q);
}
else
insert(tree[rt].r, mid + 1, r, q);
}
int query(int rt, int l, int r, int ql, int qr)
{
if (rt == 0)
return 0;
if (l >= ql && r <= qr)
return tree[rt].res;
int mid = l + r >> 1;
int cnt = 0;
if (ql <= mid)
cnt += query(tree[rt].l, l, mid, ql, qr);
if (qr > mid)
cnt += query(tree[rt].r, mid + 1, r, ql, qr);
return cnt;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int x, y;
int ans = 0;
cin >> n >> x >> y;
insert(root, -1e18, 1e18, 0);
for (int i = 1; i <= n; i++)
{
int k;
cin >> k;
sum[i] = sum[i - 1] + k - y;
ans += query(root, -1e18, 1e18, sum[i] - x, 1e18);
// cout << 1 << endl;
insert(root, -1e18, 1e18, sum[i]);
// cout << 0 << endl;
}
cout << ans;
}
E:等我学完计算几何再回来补题
H:正常要维护的话,比如维护为0,就是将1的情况均变为0,考虑什么时候会更优:如:
11111011111 此时假如对每串1翻转的话要4,假如我们对0翻转后再整体翻只要3,同理:
11110111101111 此时最下仅要4,但当出现111101111011110001111011110111此时两串就被分割开来,所以我们只要存在两个连续1中间有0就将0翻转,最后再整体翻转一下,思路比较好想,但实现起来比较难:
同时我们要处理101这类情况,因为此时我们翻两个1更划算
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
using namespace std;
#define int long long
string s;
int len;
int solve(char m)//代表将全部维护为!m的,考虑m=1,我们此时要将其维护为0
{
int ans = 0;
vector<pair<int, int>>pii;
for (int i = 0; i < len; i++)
{
if (s[i] == m)//代表此时找到了
{
int j = i;
while (s[j] == m&&j<len)
j++;
j--;
//此时{i,j}均为m(1)
if (pii.size() && i - pii.back().second - 1 == 1 && j-pii.back().first+1> 3)//第二个代表此时两串连续的1中间有一个0,第三个表示他们的长度大于3即101
{
ans++, pii.back().second = j;//将这个0翻转为1,再将整个串看为1继续操作
}
else
pii.push_back({ i, j });
i = j;
}
}
//现在处理1
int len = pii.size();
for (int i = 0; i < len; i++)
{
if (pii[i].second == pii[i].first)//代表此时长度仅为1
{
ans++;
}
else
ans += 2;
}
return ans;
}
signed main()
{
cin >> s;
len = s.length();
int ans = min(solve('0'), solve('1'));
cout << ans;
}
K:线段树,其实这道题就是我们带标记的线段树 改一下标记就好了,可以直接和带区间修改加法线段树相比较会发现基本操作一模一样
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 2e5 + 10;
struct node
{
int l, r;
int rev;
int sum1,sum2;//sum1 奇数总数
}tree[maxn<<2];
int a[maxn];
void pushdown(int rt)
{
tree[rt].sum1 = tree[rt << 1].sum1 + tree[rt << 1 | 1].sum1;
tree[rt].sum2 = tree[rt << 1].sum2 + tree[rt << 1 | 1].sum2;
}
void build(int rt, int l, int r)
{
tree[rt].l = l, tree[rt].r = r;
int mid = l + r >> 1;
if (l == r)
{
tree[rt].sum1 =a[l];
return;
}
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
pushdown(rt);
}
void upshdown(int rt)
{
if (tree[rt].rev)//代表要变
{
tree[rt << 1].rev ^= 1;
int cmp = tree[rt<<1].sum1;
tree[rt<<1].sum1 = tree[rt<<1].sum2 / 2;
tree[rt<<1].sum2 = cmp * 2;
tree[rt << 1 | 1].rev ^= 1;
cmp = tree[rt << 1|1].sum1;
tree[rt << 1|1].sum1 = tree[rt << 1|1].sum2 / 2;
tree[rt << 1|1].sum2 = cmp * 2;
tree[rt].rev = 0;
}
}
void modify(int root,int ql,int qr,int l, int r)
{
int mid = l + r >> 1;
if (ql == l && qr == r){
tree[root].rev ^= 1;
int cmp = tree[root].sum1;
tree[root].sum1 = tree[root].sum2 / 2;
tree[root].sum2 = cmp * 2;
return;
}
upshdown(root);
if (qr <= mid)
{
modify(root << 1, ql, qr, l, mid);
}
else if(ql>mid)
{
modify(root << 1 | 1, ql, qr, mid + 1, r);
}
else
{
modify(root << 1, ql, mid, l, mid);
modify(root << 1 | 1, mid + 1, qr, mid + 1, r);
}
pushdown(root);
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)cin >> a[i];
build(1, 1, n);
for (int i = 1; i <= m; i++)
{
int l, r;
cin >> l >> r;
modify(1, l, r, 1, n);
cout << tree[1].sum1 + tree[1].sum2<<'\n';
}
}
I:比较明显的数位dp
一般数位dp有两种处理方法 dfs和预处理
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
int n;
int cnt = 0;
void dfs(int x, int sum, int len)//x代表此时的数的总和,sum代表此时数的和,len代表此时数的长度
{
if (x > n)return;
if (sum > 20)return;//有效剪枝
if (x&&sum == 2 * len)cnt++;
for (int i = 0; i <= 9; i++)
{
int y = 10 * x + i;
if (y&&y <= n)
dfs(y, sum + i, len + 1);
}
}
signed main()
{
cin >> n;
dfs(0, 0, 0);
cout << cnt;
}