2022河南萌新联赛第(二)场:河南理工大学 D,H,I,K

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值