牛客小白月赛:52

C:

这类题目还是有比较统一的做法:首先对于区间覆盖这个操作,我们可以利用差分来思考:

如:[l,r]加1,变为:a[l]++,a[r+1]--然后我们在最后从头往后扫描一次就变为整体被覆盖的次数了,这种做法还可以利用到树上路径+1

#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 1e6 + 10;
int a[maxn];
int n, m;
signed main()
{
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		int op;
		cin >> op;
		if (op == 1)
		{
			int x, y;
			cin >> x >> y;
			a[x]++, a[y + 1]--;
		}
		else if (op == 2)
		{
			int x;
			cin >> x;
			a[x]++, a[n + 1]--;
		}
		else
		{
			int x;
			cin >> x;
			a[1]++, a[x + 1]--;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		a[i] += a[i - 1];
	}
	int maxx = 0;
	for (int i = 1; i <= n; i++)
	{
		maxx = max(maxx, m-a[i]);
	}
	cout << maxx << ' ';
	int cnt = 0;
	for (int i = 1; i <= n; i++)
	{
		if (m - a[i] == maxx)
		{
			cnt++;
		}
	}
	cout << cnt;

}

D:

 这里是两种算法的叠加,对于第一种我们可以比较快速的想到双指针,对于第二个我们可以利用倍增来处理,复杂度过的去,不过题目有要求最多n个,要转移l,r的长度

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 4e5 + 10;
int a[maxn],b[maxn];
int f[maxn][32];
int n, m;
void st()
{
	for (int i = 1; i <2*n; i++)
		f[i][0] = b[i];
	int k = log2(n);
	for (int j = 1; j <= k; j++)
	{
		for (int i = 1; i <= 2*n- (1ll << j) ; i++)
		{
			f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
		}
	}
}
int find_max(int l, int r)
{
	int k = log2(r - l + 1);
	return max(f[l][k], f[r - (1ll << k) + 1][k]);
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		{	cin >> a[i];
		a[i + n] = a[i];
		}
	}
	for (int i = 1; i <= n; i++)
	{		cin >> b[i];
	b[i + n] = b[i];
	}
	int ans = 1e18;//设为1e9会错
	st();
	int l = 1, r = 1;
	int sum =0;
	while (l <=n && r <=2*n){
		while (sum <m&&r<=2*n){
			sum += a[r];
           r++;
		}
		if (sum >= m&&r-l<=n){//保证长度正确
			ans = min(ans, find_max(l, r-1));
		}
		else if(sum<m){
			break;
		}
		sum -= a[l++];
	}
	if(ans!=1e18)
	cout << ans;
	else
	{
		cout << -1;
	}
}

E:

我们需要根据我们已知的数来与之前的数进行匹配,这种体型还是比较常见的,通常都是利用线段树将前面出现的数来标记,再查找,因为没法离散化,我们复杂度为 0(1e6*log2*(1e9))可以卡过去 :多交几次就好

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define int long long
const int maxn = 1e6 + 10;
const int mod = 998244353;
struct node
{
	int l, r;
	int val;
}tree[maxn*40];
int tot, n, l, r,root;
void pushdown(int rt){
	tree[rt].val = tree[tree[rt].l].val + tree[tree[rt].r].val;
	tree[rt].val%=mod;
}
void modify(int&rt, int q, int p, int l, int r){
	if (!rt)
		rt = ++tot;
	if (l == r)
	{
		tree[rt].val += p;
		tree[rt].val%=mod;
		return;
	}
	int mid = l + r >> 1;
	if (q <= mid)
		modify(tree[rt].l, q, p, l, mid);
	else
		modify(tree[rt].r, q, p, mid + 1, r);
	pushdown(rt);
}
int query(int&rt, int ql, int qr, int l, int r){
	if (!rt)
		return 0;
	if (ql == l && qr == r)
		return tree[rt].val;
	int mid = l + r >> 1;
	if (qr <=mid)
	{
		return query(tree[rt].l, ql, qr, l, mid);
	}
	else if (ql > mid)
	{
		return query(tree[rt].r, ql, qr, mid + 1, r);
	}
	else
	{
		int ret = query(tree[rt].l, ql, mid, l, mid);
		ret += query(tree[rt].r, mid + 1, qr, mid + 1, r);
		return ret%mod;
	}
}
int a[maxn];
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int m;
	cin >> n >> m;
	int ans = 0;
	int len = 1e9 + 10;
	for (int i = 1; i <= n; i++){
		int k; cin >> k;
		int cnt = 0;
		for (int j = 1; j <= k; j++)
		{
			int x; cin >> x; a[++cnt] = x;
			if (x >= m)
				ans = (ans + query(root, 1, len, 1, len)) % mod;
			else
				ans = (ans + query(root, m - x, len, 1, len)) % mod;
		}
		for (int j = 1; j <= k; j++)
		{
			modify(root, a[j], 1, 1, len);
		}
	}
	cout << ans;
}

 F:

我们设dp[i][j]代表前i个人造成伤害为j的方案数,f[i]代表这个人利用i能量最多可以造成多大伤害(因为我们的方案为至少一个人的能量不同)可以这样理解:因为我们的最后组合假如仅看每个人利用的能量来看的话,一个人能量变化导致方案数增加当前仅当变为不同的能量,这个过程就像每个人(这个人有不同的能量消耗组合)任意选取能量消耗,这个组合满足条件

写法上有很多坑:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int maxn = 3e3 + 10;
const int mod = 998244353;
int dp[maxn][maxn];
int f[maxn];
int n, m;
int a[maxn], b[maxn];
signed main()
{
	cin >> n >> m;
	dp[0][0] = 1;//方案数的基本要求
	for (int i = 1; i <= n; i++){
		int len;
		cin >> len;
		for (int j = 1; j <= len; j++)cin >> a[j];
		for (int j = 1; j <= len; j++)cin >> b[j];
		memset(f, 0, sizeof f);
		for (int j = 1; j <= len; j++){
			for (int k = 3000; k >= b[j]; k--){
				if (f[k - b[j]]|| k == b[j])//满背包的做法,也可以将初始变为负inf,f[0]=0来转移
					f[k] = max(f[k], f[k - b[j]] + a[j]);
			}
		}
		//利用f[]来转移
		for (int j = 0; j <=3000; j++){
			if (f[j] == 0&&j!=0)//利用0来将前i-1的法案数转移到i
				continue;
			if (f[j] > m)f[j] = m;//保证不会溢出
			for (int k =0;k<f[j];k++)
				dp[i][m] = (dp[i][m] + dp[i - 1][m - k])%mod;//由于dp[i][m]不太一样此时m代表不低于>m
			for (int k =m; k >= f[j]; k--){//>=
				dp[i][k] = (dp[i][k] + dp[i - 1][k - f[j]])%mod;
			}
		}
	}
	cout << dp[n][m];
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值