首页 > 其他分享 >2024.10.21 杂题

2024.10.21 杂题

时间:2024-10-21 15:09:32浏览次数:1  
标签:2024.10 return 21 int max mid 一黑 youyou 杂题

2024.10.21 杂题

P11217 【MX-S4-T1】「yyOI R2」youyou 的垃圾桶

\(O(n \log n)\) 线段树二分不会,想写 \(O(q \log ^ 2n)\) 的二分,但是 htdlz 说常数大可能过不去。所以我选择写树状数组实现的 \(O(q \log^2 n)\) 做法然后跑的飞快比线段树二分还快直接过了(doge)

记录前缀和 \(s[i]\),由于我们写的树状数组没有建树操作。所以对于每一次 \(add(l,r,k)\) 我们的实际 \(\sum a_i\) 是 \(s[n]+ask(n)\)。用 \(cnt\) 记录我们能被桶踹多少轮,由于每一波伤害翻倍。所以 \(cnt\) 为 \(\log_{2}{\frac{w}{s[n]+ask(n)}}\)。再记录被踹到最后一轮剩了多少血。如果刚好被踹死,答案就是 \(cnt \times n - 1\)。如果还有一点血,就二分计算在下标为多少的时候被踹死。二分是 \(\log n\) 的,但是每次二分都要调用 \(ask\),\(ask\) 也是 \(\log n\) 的,所以总复杂度 \(O(n \log ^ 2n)\)

#include <bits/stdc++.h>

#define rint register int
#define int long long
#define endl '\n'

using namespace std;

const int N = 2e5 + 5;

int n, q, w;
int a[N];
int s[N];

struct BIT
{
    int c[2][N];
	
	int lowbit(int x){return x & -x;}
	
	void _add(int k, int x, int y) 
	{
		for (; x <= n; x += lowbit(x)) 
		    c[k][x] += y;
	}
	
	int _ask(int k, int x) 
	{
		int res = 0;
		for (; x; x -= lowbit(x)) 
		    res += c[k][x];
		return res;
	}
	
	void add(int l, int r, int k)
	{
		_add(0, l, k), _add(0, r + 1, -k);
		_add(1, l, l * k), _add(1, r + 1, -(r + 1) * k);	
	}
	
	int ask(int x)
	{
		return (x + 1) * _ask(0, x) - _ask(1, x);
	}	
} tree;

int calc(int x, int t) 
{
	int l = 1, r = n;
	while (l < r) 
	{
		int mid = (l + r) >> 1;
		if ((s[mid] + tree.ask(mid)) * t >= x) r = mid;
		else l = mid + 1;
	}
	return l;
}

signed main() 
{
	ios::sync_with_stdio(0); 
	cin.tie(0), cout.tie(0);
	cin >> n >> q >> w;
	for (rint i = 1; i <= n; i++) 
	{
		cin >> a[i];
		s[i] = s[i - 1] + a[i];
	}
	while (q--) 
	{
		int l, r, k;
		cin >> l >> r >> k;
        tree.add(l, r, k);
		int p = s[n] + tree.ask(n);
		// 因为原来的答案并没有加入到树状数组里 所以为 s[n]+ask(n)
		int cnt = 0;//记录能打多少轮
		cnt = log2(w / p + 1);
		int ck = w - ((1ll << cnt) - 1) * p;//看看打完后剩多少血
		if (!ck) //刚好死掉
		{
			cout << cnt * n - 1 << endl;
			continue;
		}
		int ans1 = calc(ck, 1ll << cnt) - 1;//二分答案计算最后一次在哪个位置死掉
		cout << cnt * n + ans1 << endl;
	}
	return 0;
}

P11218 【MX-S4-T2】「yyOI R2」youyou 不喜欢夏天

观察数据范围,复杂度瓶颈不能超过 \(n \log n\)。想带个 \(\log\) 又不能排序又不能数据结构维护。那正解应该是 \(O(n)\) 的。考虑贪心,只能观察出一些性质。

以一列为单位,先不考虑是不是联通块,对于两黑或两白是简单的,两黑一起选了一定更优,两白一定尽可能不选。对于一黑一白有两种玩法,第一种是如果 \(m\) 特别小 \(n\) 特别大,那么尽可能多的选一黑一白中的黑,不选白。因为多选一个黑就能赚一点,\(m\) 很小 yy 不能怎么改变结局。或者一黑一白一起选了 yy 就算翻转了对答案也没影响。

问题在于,什么时候选择一黑一白策略?什么时候选择一黑策略?怎么判断是不是联通块?贪心显然是不行了。由于是一列一列扫的,每一列都有很强的关联性。并且对于联通块左边界 \(i\),从 \(i\) 开始扫,如果答案不优秀了要把联通块断开重新算,dp 可以维护这个。所以考虑进行 dp。

\(f_i\) 表示在选择一黑一白的时候两个一块办了的答案,\(g_i\) 表示在选择一黑一白的时候只选择一个黑的答案。数组 w[i] = (c[i] - '0') * 2 + (d[i] - '0') 以此来记录当前列的形式。

如果 \(w_i=3\),那么 \(f_i=f_{i-1}+2\),\(g\) 的转移一样。

如果 \(w_i = 0\),那么只选一个白的作为过渡或者直接断开重新开始,\(f_i = max\{f[i - 1] - 1, 0\}\),\(g[i]\) 同理

如果 \(w_i=1/2\),那么 \(f_i=f_{i-1}\),因为选上这一列对答案没影响。而对于 \(g\) 考虑的就多了,特判如果出现了两个白隔断不能进行只选一个的操作 ,以及两种不同的一黑一白交替出现了导致只选黑色不能连起来,没有这些这些情况 \(g_i = g_{i - 1} + 1\),否则 \(g_i=g_{i-1}\)。开个 \(lst\) 辅助判断情况。

对于 \(f_i\) ,最后的答案就是 \(f_i\),而对于 \(g_i\),由于有很多一黑一白只选一个黑的情况,yy 可以通过翻转操作让他变成白从而让答案减小 \(2\),所以 \(g_i\) 最后的答案要减去一个 \(2\times m\)。

对于最终的答案就是 \(\max(\max_{i=1}^{n}(g_i-2\times m),\max_{i=1}^{n}f_i)\)

诶,问题来了,我们的 \(g\) 转移其实是有问题的,因为我们可能中间选择的一黑一白选一黑的情况并不够 \(m\) 次导致最后计算出来的答案偏小。没关系哒,因为我们最终的答案是对多个答案取最大值,而我们的考虑不周只是会导致部分 \(g_i\) 算的比较小,但是不会影响最终结果。

复杂度 \(O(n)\)

#include <bits/stdc++.h>

#define rint register int
#define int long long
#define endl '\n'
#define m(a) memset(a, 0, sizeof a)

using namespace std;

const int N = 2e6 + 5;
const int inf = 1e9;

int C, T, n, m;
int a[N], b[N];
int w[N];
int f[N], g[N];
/*
f[i] 表示在选择一黑一白的时候两个一块办了的答案
g[i] 表示在选择一黑一白的时候只选择一个黑的答案
*/
char c[N], d[N];

signed main() 
{
	cin >> C >> T;
	while (T--) 
	{
		cin >> n >> m;
		int lst = 0, ans = 0;
		scanf("%s%s", c + 1, d + 1);
		for (rint i = 1; i <= n; i++) w[i] = (c[i] - '0') * 2 + (d[i] - '0');
		m(g), m(f);
		for (rint i = 1; i <= n; i++) 
		{
			if (w[i] == 3)
			{
				f[i] = f[i - 1] + 2;
				g[i] = g[i - 1] + 2;
				lst = 3;				
			}
			else if (w[i] == 0) 
			{
				f[i] = max(f[i - 1] - 1, 0ll);
				if (g[i - 1] > 1) g[i] = g[i - 1] - 1;
				else g[i] = 0, lst = 3;
			} 
			else 
			{
				f[i] = f[i - 1];
				//这个时候为一黑一白 f[]两个一起选了 答案不变
				if ((lst + w[i]) != 3)
				/*
				特判如果出现了两个白隔断不能进行只选一个的操作 
				以及两种不同的一黑一白交替出现了导致只选黑色不能连起来
				没有这些这些情况 g[i] = g[i - 1] + 1
				*/
				{
					g[i] = g[i - 1] + 1;
					lst = w[i];					
				}
				else 
				{
					g[i] = g[i - 1];
					lst = 3;
				}
			}
			ans = max({ans, g[i] - 2 * m, f[i]});
		}
		cout << ans << endl;
	}
	return 0;
}

P11219 【MX-S4-T3】「yyOI R2」youyou 的序列 II

感谢 ReTF 提供的帮助

首先,如果询问的区间中含有大于 \(w_1\) 的数字,那么 youyou 必然失败。

称长度为 \(c_2\),总和大小大于 \(w_2\) 的子区间为合法区间,也即 yy 可以操作的区间。

  • 性质 1:对于不存在任何一个“合法区间”的序列,youyou 显然必胜
  • 性质 2:存在“合法区间”,若 youyou 可以一次性染红所有未染红的合法区间,则 youyou 必胜
  • 性质 3:存在“合法区间”,若 youyou 不可以做到一次性染红所有未染红的合法区间,则 yy 必胜

对于 yy 而言的最优策略是:尽量在整个序列的边缘进行染色。yy 的目的是防止 youyou 把整个序列染成红色。设所有“合法区间”中位于最左边的左端点为 \(l\),最右边的右端点为 \(r\)。如果满足性质 3,只考虑位置 \(l,r\) ,若 youyou 每次把哪个点染了,yy 就可以跟着染,然后陷入循环,youyou 必败。所以只要 youyou 无法一次染红 \(l\) 和 \(r\) ,则 yy 必胜。

直接维护原序列使用线段树即可,非常简单。实现瓶颈在于如何求出上述的 \(l,r\)

粉兔讲的是线段树二分,每个叶子记录一个长度为 \(c_2\) 的区间,将单点修改转为区间修改。

这里采用的实现方式是使用势能线段树,每个点维护的是 [i,i + d - 1] 的元素和。那么当当前点势能为 \(0\) 时并且 \(l=r\),那么将 [i,i + d - 1] 存入即可。存储可以开个 set。由于每次 change 只会在 setinsert 一次,所以复杂度为 \(O(n \log n)\)

补充:关于势能线段树

之前并没有整理过相关笔记。马上退赛了也必要专门拿出来时间做笔记,大概记录一下原理。很多区间修改操作是不能依靠懒标记完成,因为很多运算都是依赖于叶子节点的值的一直递归到叶子结点一个一个改显然无法接受。每一个操作,总会使得其能够接受的继续进行修改的次数越来越少。比如一开始位于高空,每次修改使高度下降势能变小,当势能为 \(0\) 时再去对接来下的操作就没有意义了可以直接停了。

#include <bits/stdc++.h>

#define rint register int
#define int long long
#define endl '\n'

#define ls p << 1
#define rs p << 1 | 1

using namespace std;

const int N = 3e5 + 5;
const int M = 1 << 20;
const int inf = 1e18;

int n, q, c1, c2, w1, w2;
int a[N];
set<int> L, R;
int s[N];

struct SegmentTree1
{
	struct node
	{
		int v, sum;
	} t[M]; 
	
	void push_up(int p)
	{
		t[p].v = max(t[ls].v, t[rs].v);
		t[p].sum = t[ls].sum + t[rs].sum;
	}
	
	void build(int p, int l, int r) 
	{
		if (l == r) 
		{
			t[p].v = t[p].sum = a[l];
			return;
		}
		int mid = (l + r) >> 1;
		build(ls, l, mid);
		build(rs, mid + 1, r);
		push_up(p);
	}

	void change(int p, int l, int r, int x, int d) 
	{
		if (l == r) 
		{
			t[p].v = t[p].sum = d;
			return;
		}
		int mid = (l + r) >> 1;
		if (x <= mid) change(ls, l, mid, x, d);
		else change(rs, mid + 1, r, x, d);
		push_up(p);
	}

	int query_sum(int p, int l, int r, int x, int y) 
	{
		if (x <= l && r <= y) return t[p].sum;
		int mid = (l + r) >> 1;
		int res = 0;
		if (x <= mid) res += query_sum(ls, l, mid, x, y);
		if (y > mid) res += query_sum(rs, mid + 1, r, x, y);
		return res;
	}

	int query_max(int p, int l, int r, int x, int y) 
	{
		if (x <= l && r <= y) return t[p].v;
		int mid = (l + r) >> 1;
		if (x <= mid) return query_max(ls, l, mid, x, y);
		if (y > mid) return query_max(rs, mid + 1, r, x, y);
		return max(query_max(ls, l, mid, x, y), query_max(rs, mid + 1, r, x, y));
	}
} tree1;

struct SegmentTree2
{
	struct node
	{
		int minn, lazy;
	} t[M]; 
	
	void push_up(int p)
	{
		t[p].minn = min(t[ls].minn, t[rs].minn);
	}

	void push_down(int p) 
	{
		t[ls].minn -= t[p].lazy, t[rs].minn -= t[p].lazy;
		t[ls].lazy += t[p].lazy, t[rs].lazy += t[p].lazy;
		t[p].lazy = 0;
	}

	void build(int p, int l, int r) 
	{
		t[p].minn = inf;
		if (l == r) return ;
		int mid = (l + r) >> 1;
		build(ls, l, mid);
		build(rs, mid + 1, r);
		push_up(p);
	}

	void pos_change(int p, int l, int r, int x, int y) 
	{
		if (l == r) 
		{
			t[p].minn = min(t[p].minn, y);
			return;
		}
		int mid = (l + r) >> 1;
		if (x <= mid) pos_change(ls, l, mid, x, y);
		else pos_change(rs, mid + 1, r, x, y);
		push_up(p);
	}

	void range_change(int p, int l, int r, int x, int y, int d) 
	{
		if (t[p].minn >= inf) return;
		if (x <= l && r <= y) 
		{
			if (t[p].minn > d)
			{
				t[p].minn -= d; 
				t[p].lazy += d;
			} 
			else if (l == r) 
			{
				t[p].minn = inf;
				L.insert(l);
				R.insert(l + c2 - 1);
			} 
			else 
			{
				push_down(p);
				int mid = (l + r) >> 1;
				range_change(ls, l, mid, x, y, d);
				range_change(rs, mid + 1, r, x, y, d);
				push_up(p);
			}
			return ;
		}
		push_down(p);
		int mid = (l + r) >> 1;
		if (x <= mid) range_change(ls, l, mid, x, y, d);
		if (y > mid) range_change(rs, mid + 1, r, x, y, d);
		push_up(p);
	}
} tree2;

int findL(int x) {return *L.lower_bound(x);}
int findR(int x) {return *--R.upper_bound(x);}

signed main() 
{
	cin >> n >> q >> c1 >> c2 >> w1 >> w2; 
	for (rint i = 1; i <= n; i++)
	{
		cin >> a[i];
		s[i] += s[i - 1] + a[i];
	} 
	if (c2 > n) c2 = n;
	tree1.build(1, 1, n);
	tree2.build(1, 1, n - c2 + 1);
	L.insert(0), L.insert(n + 1);
	R.insert(0), R.insert(n + 1);
	for (rint i = 1; i <= n - c2 + 1; i++) //枚举左端点
	{
		int sum = s[i + c2 - 1] - s[i - 1];//区间和
		if (sum <= w2) tree2.pos_change(1, 1, n - c2 + 1, i, w2 - sum + 1);
		//tree2 是一颗势能线段树
		//每个点维护的是 [i,i + d - 1] 的元素和
		else L.insert(i), R.insert(i + c2 - 1);//大于 w2 全选更优
	}
	while (q--) 
	{
		int opt;
		cin >> opt;
		if (opt == 1) 
		{
			int x, y;
			cin >> x >> y;
			a[x] += y;
			tree1.change(1, 1, n, x, a[x]);
			if (max(x - c2 + 1, 1ll) <= min(x, n - c2 + 1)) 
				tree2.range_change(1, 1, n - c2 + 1, max(x - c2 + 1, 1ll), min(x, n - c2 + 1), y);
			//单点修改同时在tree2更新存储 l, r
		} 
		else 
		{
			/*
			称长度为 c2,总和大小大于 w2 的子区间为合法区间 也即 yy 可以操作的区间
			*/
			int l, r;
			cin >> l >> r;
			if (tree1.query_max(1, 1, n, l, r) > w1) puts("tetris");
			// youyou 没法出手直接输掉
			else 
			{
				if (r - l + 1 <= c2) 
				{
					if (tree1.query_sum(1, 1, n, l, r) > w2) 
					// 如果 yy 能一次性全染上
					{
						if (r - l + 1 <= c1 && tree1.query_sum(1, 1, n, l, r) <= w1) puts("cont");
						// 如果 youyou 能一次全染上 则 youyou 一定能赢
						else puts("tetris");
						// 否则 yy 一直一次性全染上 youyou 赢不了
					} 
					else puts("cont");
					// yy 不能一次性全部解决掉,则 youyou 一定赢
				} 
				else 
				{
					int L = -1, R = -1;
					L = findL(l), R = findR(r);
					// 查找当前情况左右端点
					if (L == -1 || R == -1 || L > R || R - L + 1 < c2) puts("cont");
					// 如果 L,R 没找到或者 L 比 R 大又或者区间长度小于 c2
					// 即不存在合法区间
					// 那么 youyou 一定赢
					else if (R - L + 1 <= c1 && tree1.query_sum(1, 1, n, L, R) <= w1) puts("cont");
					// 同样,如果 youyou 能一次全染上 则 youyou 一定能赢
					else puts("tetris");
					// 否则 yy 赢
				}
			}
		}
	}
	return 0;
}

标签:2024.10,return,21,int,max,mid,一黑,youyou,杂题
From: https://www.cnblogs.com/spaceswalker/p/18489532

相关文章

  • 20241021比赛总结
    T1岛屿https://www.gxyzoj.com/d/hzoj/p/4177显然每个点只增加了一条边,最终每个点的度数都为2,所以最终必然是很多个环,连边的过程中,也必然是一些链和一些环由题,蓝同色链的个数和红同色链的个数相等,所以设\(f(a,b)\)为a条红同色链,b条异色链的期望考虑先处理异色链:红红连红蓝为......
  • CH9121_MQTT应用
    参考代码程序下载:https://files.cnblogs.com/files/blogs/808422/EXAM_mqtt_912x.zip?t=1729489963&download=true前言:(1)很多物联网\嵌入式应用需要将采集的数据上传到MQTT服务器以实现集中实时管理。然而可能前期选型时并未考虑到这一点导致选用的MCU没有网络功能无法实现。并且......
  • 【进阶OpenCV】 (21) --卷积神经网络实现人脸检测
    文章目录卷积神经网络实现人脸检测一、加载CNN人脸检测模型二、图像预处理三、绘制人脸矩形框总结卷积神经网络实现人脸检测opencv可以直接通过readnet来读取神经网络。dlib也可以的。任务:使用dlib库中的卷积神经网络(CNN)人脸检测模型来检测一张图片中的人脸,并使用O......
  • ChatGPT-4国内中文版镜像网站整理合集(2024/10/21)
    ​绝对好用的收集ChatGPT镜像网站的开源项目镜像站收集开源项目chatgpt-mirror-site 收集各种可以的ChatGPT镜像网站,免费的收费的。支持4o以及o1,支持MJ绘画一、GPT中文镜像站1.什么是镜像站镜像站(MirrorSite)是指通过复制原始网站内容和结构,创建的备用网站。其主要目的是在......
  • 10.21课堂
    教案:沪教版牛津英语4AM1U3《Howdoyoufeel?》一、教材分析本单元通过“情感”这一主题,引导学生学习和使用描述情感的词汇和句型。教材设计注重情感表达的实际运用,结合生活场景,帮助学生理解不同情感的表达方式。此外,课文中的对话和活动也鼓励学生参与互动,提升口语表达能力。二......
  • 设置显示或者隐藏MasterSeeker和Total Commander主窗口的快捷键的AutoHotkey脚本2024.
    设置显示或者隐藏MasterSeeker和TotalCommander主窗口的快捷键的AutoHotkey脚本2024.10.21=========  ;========设置显示或者隐藏MasterSeeker和TotalCommander主窗口的快捷键的AutoHotkey脚本2024.10.21=========;此脚本从此行开始;D:\app\RegHotkey\RegHotkey.a......
  • 【日记】什么叫做大人呢?(2108 字)
    正文昨天买了一桶酸奶。新希望。感觉没有之前光明的好喝。价签上写的是12.9,但是结帐的时候给了14.78。我觉得很奇怪,问了收银员。收银员说奶制品8.8折。我说跟这个没关系,价钱和扣款不一致。她也觉得很奇怪,拿着我的小票专门跑去看了一下。活动日期和商品名都对,就是价格标错......
  • 10.21
    大学为进一步推进无纸化考试,研开发一考试统。系统管理员能够创建专业方向、课程编号、任课教师等相关考试基础信息。教师和者生进行考试相关工作。系统与考试系统与考试有关的主要功能如下:(1)考试设置:教师设置试题(题目和答案),制定考试说明。考试时间和提醒时间等考试信息,录入参......
  • 2024.10.20 杂题
    P11208『STA-R8』轮回疯狂只执行操作一就是逆序对的个数,统计对于每一个\(a_i\)的逆序对个数为\(b_i\),然后模拟执行删除操作,如果删除操作比换位操作更优就更新答案。复杂度\(O(n\logn)\)record将最小的删除可以等价成往里加最大的数,倒着模拟即可。至于操作一,每次往数......
  • SP9685 ZTC - Zombie’s Treasure Chest 题解
    洛谷题目传送门双倍经验简单题。对于空间大小为\(s1\timess2\)时,显然最多可得到的价值为\(\max(s2\timesv1,s1\timesv2)\),剩下小于\(s1\timess2\)的部分选一个占用空间大的枚举就好。时间复杂度:\(O(T\lfloor\frac{m}{\max(s1,s2)}\rfloor)\),其中\(m=n\bmo......