首页 > 其他分享 >【题解】第五届图灵杯

【题解】第五届图灵杯

时间:2023-05-18 23:57:06浏览次数:45  
标签:opt nxt lc int 题解 图灵 dep lst 第五届

// created on 23.05.13

目录

A. 差值

考虑求长度为 \(i\) 的答案,肯定是长度 \(\geq i\) 的后缀,按照字典序排序后,相邻两个求解。

所以考虑倒着扫,每次对于 \(i\) 的后缀找到排名前后第一个后缀,求出两个长度为 \(i\) 的解。更新解(比大小)的部分用哈希和二分解决即可。

然后最后只要倒着贡献,用 \(i+1\) 的最优解贡献 \(i\) 即可(非 \(i+1\) 最有解一定无用)。

时间复杂度 \(O(n\log n)\) 。

B. 基础循环结构练习题

考虑非严格递增的部分,令 \(d_i=b_i-b_{i-1}\),枚举 \(i\) 从 \(n\) 到 \(1\),设计如下策略:

  • 此时 \(i\) 是全局最大值,通过一次 mod 将其变为 \(0\) 。
  • 全局加 \(d_i\) 。

注意到每次 mod 不会影响后缀,并且一个位置最终的值就是前侧 \(\sum d\) 。我们用 \(2n\) 次操作达到了目的。

如果非递增的话,考虑最后一步的 mod 。倒着做,相当于用 \(c=\max b+1\) 来调整 \(b\) 使得其为非严格递增即可。恰好只要多一次操作。

C. 基础计算几何练习题

观察一下,我们将点对按照点对间距离排序,每次取出距离最小的点对集合 \(S=\{(x_1,y_1),(x_2,y_2),\cdots\}\),然后对于 \(x_i,y_i\),如果之前两者答案都是 false,那么一起改成 true

怎么观察出来的?从链考虑,先考虑间隔最小的,肯定一步到位全是 true 。然后是次小的,如果和最小不相交也是 true 了,否则肯定不操作,不然就输了。

那么至此可以写出 50pts 。

我们用 KDT 维护每个点与周围 未删除点 的最小距离,并打包成二元组存入堆。每次取出堆头,检查最小距离是否是对的(可能与之形成最小距离的点已经被删了)。在踢掉所有非法的最小距离后,就得到了当前全局最小距离。接着不断地取出,拥有全局最小距离的点,并且这一轮我们会将这些点全部删除。如此往复直到点剩下小于两个。

时间复杂度未知(我们只需要注意一个点被错误取出的次数,感受一下这个次数很小,不知道能不能和平面最近点对一样分析?),但其实速度还行。

但其实有更好的做法:将网格划分成若干个 \(2^i\times 2^i\) 的格子,每次一个格子里只剩下至多一个点,查询时只需要考虑格子周围的格子中的点即可。

即,从 \(i=0\) 开始做,每次将 \(O(n)\) 个平面最近点对拿出来按照距离排序,然后按照暴力的方式模拟即可。此时 \(2^{i+1}\times 2^{i+1}\) 的格子中至多剩下一个点,否则在这一步肯定消掉了。于是,我们得到了 \(O(n\log V)\) 的做法。

D. 永恒悲剧

对于一个位置 \(1\leq i\leq n\) 而言,一个右端点 \(r\) 存在 \(p\leq r\) 满足合法左端点为 \(l\leq p\) 且稳定值一样。

\(p\) 的要求是:可以在 \([p+1,r]\) 中找到一个,opt 不同操作相交的位置 \(p'\) 。我们记 \(nxt_p\) 表示 \(p\) 后第一个满足要求的位置,\(lst_p\) 表示 \(p\) 前第一个满足要求的位置。

那么对于 \(r\) 而言有 \(p=\max\{lst_1,lst_2,\cdots,lst_r\}\) 。当有多个 \(i\) 的 \(lst_i\) 相同时,显然只要保留最小的 \(i\) 。此时若保留的 \(i\) 为 \(t\),那么 \(nxt_{lst_t}=t\) 也是必然成立的。对于 \(i,j\) 若 \(lst_i=j,nxt_j=i\) 就记 \((j,i)\) 为一个 "稳定对" 。

不妨假设 \(p\) 是 max 操作,那么最后 \(r\) 得到的贡献为 \(p\times \min\{c_{p+1},\cdots,c_r\}\) 。

外层直接上线段树分治,修改 / 撤销 \(lst_i,nxt_i\):每次插入 \(i\) 只要找到 \(lst_i,nxt_i\) 并检查是否形成 "稳定对" 即可。到了叶子就做一遍贡献(即对 \(ans_1,ans_2,\cdots,ans_r\) 贡献答案)。

于是问题来到怎么处理答案的贡献。考虑用线段树维护,并定义如下函数:

  • \(\mathbf{push}(x,l,r,p,lim_c,c)\) 表示:\([x,l,r]\) 节点,前侧 \(lst_i\) 的最大值为 \(p\),\(p\) 后续 \(c\) 的最小值(或最大值)为 \(lim_c\) 。一共要对 \(ans_i,l\leq i\leq r\) 贡献 \(c\) 次。
  • \(\mathbf{push_{fix_p}}(x,l,r,type,lim_c,c)\) 表示:在 \(p\) 已经不会在 \([x,l,r]\) 中改变时,\(type\) 表示 \(p\) 的类型,\(lim_c\) 同理,\(c\) 同理(为了方便,可以把 \(p\) 乘入 \(c\) 中)。

在一次 \(\mathbf{push}(x,l,r,p,lim_c,c)\) 中:

  • 如果到了叶子,更新并直接算贡献。
  • 如果左侧 \(\max lst_i\) 比 \(p\) 大,那么往右侧打标记表示 "右侧节点被计算 \(c\) 次贡献"。此时 \(p,lim_c\) 不会影响右侧节点,递归进入左侧。
  • 否则,左侧区间中 \(p\) 不会改变,用 \(\mathbf{push_{fix_p}}\) 递归计算贡献。然后再递归进入右侧继续计算。

在一次 \(\mathbf{push_{fix_p}}(x,l,r,type,lim_c,c)\) 中:

  • 如果到了叶子,更新并直接算贡献。
  • 如果左侧对 \(lim_c\) 没有影响,那么所有位置都加上 \(lim_c\times c\),打标记处理。
  • 否则,递归进入左侧计算。此时到右侧时,\(lim_c\) 只取决于左侧,所以可以在 \(x\) 打标记表示 " \(type\) 固定,用左侧对应 \(lim_c\),递归进入右侧贡献系数加上 \(c\) "。

\(\mathbf{push_{fix_p}}\) 是 \(O(\log m)\) 的,\(\mathbf{push}\) 是 \(O(\log^2 m)\) 的。一共 \(O(m\log n)\) 次单点修改,于是 pushdown 的次数是 \(O(m\log n\log m)\) 。每次 pushdown 需要根据标记用 \(\mathbf{push},\mathbf{push_{fix_p}}\) 计算代价,那么复杂度是 \(O(m\log n\log^3 m)\) 的,但是常数较小。

// 
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(...) fprintf (stderr, __VA_ARGS__)

#define fi first
#define se second
#define lep(i, l, r) for (int i = (l), i##_end = (r); i <= i##_end; ++ i)
#define rep(i, r, l) for (int i = (r), i##_end = (l); i >= i##_end; -- i)

char _c; bool _f; template <class T> inline void IN (T & x) {
	x = 0, _f = 0; while (_c = getchar (), ! isdigit (_c)) if (_c == '-') _f = 1;
	while (isdigit (_c)) x = x * 10 + _c - '0', _c = getchar (); if (_f) x = - x;
}

template <class T> inline void chkmin (T & x, T y) { if (x > y) x = y; }
template <class T> inline void chkmax (T & x, T y) { if (x < y) x = y; }

const int N = 1e5 + 5;
const int INF = 1e9;

int n, m;
ll ans[N];
pair <int, int> opt[N];

// {{{ segment tree

#define lc ((x) << 1)
#define rc ((x) << 1 | 1)
#define mid ((l + r) >> 1)

bool act[N];

struct node {
	int mi_c, mx_c, p, p_c;
	ll l_to_r, fix_p[2], ans;
} t[N << 2];

void build (int x, int l, int r) {
	t[x].mi_c = INF, t[x].mx_c = -1;
	if (l == r) return t[x].p_c = opt[l].fi, void ();
	build (lc, l, mid), build (rc, mid + 1, r);
}
inline void pushup (int x) {
	t[x].mi_c = min (t[lc].mi_c, t[rc].mi_c);
	t[x].mx_c = max (t[lc].mx_c, t[rc].mx_c);

	if (t[lc].p > t[rc].p) {
		t[x].p = t[lc].p;
		if (opt[t[x].p].se == 1) t[x].p_c = min (t[lc].p_c, t[rc].mi_c);
		if (opt[t[x].p].se == 2) t[x].p_c = max (t[lc].p_c, t[rc].mx_c);
	} else {
		t[x].p = t[rc].p, t[x].p_c = t[rc].p_c;
	}
}

// {{{ pushdown 

void push_fix_p (int x, int l, int r, int type, int p_c, ll buf) {
	if (l == r) {
		if (act[l]) {
			if (type == 1 && opt[l].se == 2) chkmin (p_c, opt[l].fi);
			if (type == 2 && opt[l].se == 1) chkmax (p_c, opt[l].fi);
		}
		ans[l] += 1ll * p_c * buf;
	} else {
		if ((type == 1 && t[lc].mi_c >= p_c) || (type == 2 && t[lc].mx_c <= p_c)) {
			t[lc].ans += 1ll * p_c * buf;
			push_fix_p (rc, mid + 1, r, type, p_c, buf);
		} else {
			t[x].fix_p[type - 1] += buf;
			push_fix_p (lc, l, mid, type, p_c, buf);
		}
	}
}
void push (int x, int l, int r, int p, int p_c, int buf) {
	if (l == r) {
		if (act[l]) {
			if (t[x].p > p) p = t[x].p, p_c = t[x].p_c;
			else {
				if (opt[p].se == 1 && opt[l].se == 2) chkmin (p_c, opt[l].fi);
				if (opt[p].se == 2 && opt[l].se == 1) chkmax (p_c, opt[l].fi);
			}
		}
		ans[l] += 1ll * p * p_c * buf;
	} else {
		if (t[lc].p >= p) {
			t[x].l_to_r += buf;
			push (lc, l, mid, p, p_c, buf);
		} else {
			if (p) push_fix_p (lc, l, mid, opt[p].se, p_c, 1ll * buf * p);

			if (! p) push (rc, mid + 1, r, p, p_c, buf);
			else {
				if (opt[p].se == 1) push (rc, mid + 1, r, p, min (p_c, t[lc].mi_c), buf);
				if (opt[p].se == 2) push (rc, mid + 1, r, p, max (p_c, t[lc].mx_c), buf);
			}
		}
	}
}

inline void pushdown (int x, int l, int r) {
	if (t[x].l_to_r) {
		push (rc, mid + 1, r, t[lc].p, t[lc].p_c, t[x].l_to_r);
		t[x].l_to_r = 0;
	}
	lep (o, 1, 2) if (t[x].fix_p[o - 1]) {
		push_fix_p (rc, mid + 1, r, o, (o == 1 ? t[lc].mi_c : t[lc].mx_c), t[x].fix_p[o - 1]);
		t[x].fix_p[o - 1] = 0;
	}
}

// }}}

int lst[N], nxt[N];

void update (int x, int l, int r, int i) {
	if (l == r) return t[x].p = lst[i], void ();
	pushdown (x, l, r);
	i <= mid ? update (lc, l, mid, i) : update (rc, mid + 1, r, i);
	pushup (x);
}
void flip (int x, int l, int r, int i) {
	if (l == r) {
		act[i] ^= 1;
		if (opt[i].se == 1) t[x].mx_c = act[i] ? opt[i].fi : -1;
		if (opt[i].se == 2) t[x].mi_c = act[i] ? opt[i].fi : INF;
	} else {
		pushdown (x, l, r);
		i <= mid ? flip (lc, l, mid, i) : flip (rc, mid + 1, r, i);
		pushup (x);
	}
}

/* lst and nxt */

int find_lst (int x, int l, int r, int i) {
	if (l > i) return -1;
	if (opt[i].se == 1 && t[x].mi_c > opt[i].fi) return -1;
	if (opt[i].se == 2 && t[x].mx_c < opt[i].fi) return -1;

	if (l == r) return l;
	int ret = find_lst (rc, mid + 1, r, i);
	return ~ ret ? ret : find_lst (lc, l, mid, i);
}
int find_nxt (int x, int l, int r, int i) {
	if (r < i) return -1;
	if (opt[i].se == 1 && t[x].mi_c > opt[i].fi) return -1;
	if (opt[i].se == 2 && t[x].mx_c < opt[i].fi) return -1;

	if (l == r) return l;
	int ret = find_nxt (lc, l, mid, i);
	return ~ ret ? ret : find_nxt (rc, mid + 1, r, i);
}

vector <pair <int, int> > s_nxt[21], s_lst[21];

inline void find_lst (int i, int & dep) {
	int p = find_lst (1, 1, m, i);
	if ( ~ p && find_nxt (1, 1, m, p) == i) {
		if (nxt[p]) {
			s_lst[dep].emplace_back ( nxt[p], p );
			lst[nxt[p]] = 0, update (1, 1, m, nxt[p]);
		}
		s_nxt[dep].emplace_back ( p, nxt[p] ), nxt[p] = i;
		s_lst[dep].emplace_back ( i, 0 ), lst[i] = p, update (1, 1, m, i);
	}
}
inline void find_nxt (int i, int & dep) {
	int p = find_nxt (1, 1, m, i);
	if ( ~ p && find_lst (1, 1, m, p) == i) {
		if (lst[p]) {
			s_nxt[dep].emplace_back ( lst[p], p ), nxt[lst[p]] = 0;
		}
		s_nxt[dep].emplace_back ( i, 0 ), nxt[i] = p;
		s_lst[dep].emplace_back ( p, lst[p] ), lst[p] = i, update (1, 1, m, p);
	}
}

inline void join (int i, int & dep) {
	flip (1, 1, m, i);
	find_lst (i, dep), find_nxt (i, dep);
}

void collect (int x, int l, int r) {
	if (l == r) return ans[l] += t[x].ans, void ();
	pushdown (x, l, r);
	if (t[x].ans) t[lc].ans += t[x].ans, t[rc].ans += t[x].ans;
	collect (lc, l, mid), collect (rc, mid + 1, r);
}

#undef lc
#undef rc
#undef mid

// }}}

// {{{ divide

#define lc ((x) << 1)
#define rc ((x) << 1 | 1)
#define mid ((l + r) >> 1)

vector <int> stk[N << 2];

void insert (int x, int l, int r, int L, int R, int i) {
	if (L <= l && r <= R) return stk[x].emplace_back (i), void ();
	if (L <= mid) insert (lc, l, mid, L, R, i);
	if (R > mid) insert (rc, mid + 1, r, L, R, i);
}
void divide (int x, int l, int r, int dep = 0) {
	for (auto & i : stk[x]) join (i, dep);

	if (l == r) push (1, 1, m, 0, 0, 1);
	else {
		divide (lc, l, mid, dep + 1);
		divide (rc, mid + 1, r, dep + 1);
	}
	for (auto & i : stk[x]) flip (1, 1, m, i);
	
	reverse (s_nxt[dep].begin (), s_nxt[dep].end ());
	reverse (s_lst[dep].begin (), s_lst[dep].end ());

	for (auto & [a, b] : s_nxt[dep]) nxt[a] = b;
	for (auto & [a, b] : s_lst[dep]) lst[a] = b, update (1, 1, m, a);

	s_nxt[dep].clear ();
	s_lst[dep].clear ();
}

#undef lc
#undef rc
#undef mid

// }}}

signed main () {
	IN (n), IN (m);
	for (int op, l, r, c, i = 1; i <= m; ++ i) {
		IN (op), IN (l), IN (r), IN (c);
		opt[i] = { c, op };
		insert (1, 1, n, l, r, i);
	}

	build (1, 1, m);
	divide (1, 1, n);

	collect (1, 1, m);
	lep (i, 1, m) printf ("%lld%c", ans[i], " \n"[i == m]);
	return 0;
}

标签:opt,nxt,lc,int,题解,图灵,dep,lst,第五届
From: https://www.cnblogs.com/qiulyqwq/p/17413645.html

相关文章

  • GYM104363 2023 ccpc 黑龙江省赛 题解
    比赛链接:https://codeforces.com/gym/104363题解:B//bySkyRainWind#include<bits/stdc++.h>#definemprmake_pair#definedebug()cerr<<"Yoshino\n"#definepiipair<int,int>#definepbpush_backusingnamespacestd;typedeflong......
  • ASC8题解
    B:考虑用\(D(n,r)\)表示在\(r\)维空间中有\(n\)个超平面最多形成多少个区域,感觉不是很好做,考虑一下怎么递推。根据在二三维的经验,我们可以得到以下递推式:\(D(n,r)=D(n-1,r)+D(n-1,r-1)\),实际意义就是原来已经有了\(n-1\)个然后再加入一个之后,会与前面的\(n-1\)个超平面在\(r\)维......
  • Html中使用jquery通过Ajax请求WebService接口以及跨域问题解决
    场景VS2019新建WebService/Web服务/asmx并通过IIS实现发布和调用:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/130743584在上面实现发布WebService的基础上,怎样在html中通过jquery对接口发起请求和解析数据。注:博客:https://blog.csdn.net/badao_liumang_qiz......
  • abc269_f Numbered Checker 题解
    NumberedChecker题意有一个\(n\timesm\)的方格矩阵,左上角是\((1,1)\),右下角是\((n,m)\),每个方格中都有一个整数,其中\((x,y)\)中的数字为:如果\(x+y\)是奇数,则\((x,y)\)中的数字为\(0\)。否则,\((x,y)\)中的数字为\((x-1)\timesm+y\)。有\(Q\)组询问,每组......
  • CSP-J2022试题题解
    1.乘方原题:https://www.luogu.com.cn/problem/P8813代码:#include<bits/stdc++.h>#definelllonglongusingnamespacestd;constintXN=1e9;lla,b,ans=1;intmain(){ cin>>a>>b; for(inti=1;i<=b;i++){ ans*=a; if(ans>XN){ cout......
  • abc270_f Transportation 题解
    Transportation题意有\(n\)个城市,你可以执行以下操作若干次:选择一个没有建机场的城市\(i\),花费\(x_i\)建一个机场。选择一个没有建港口的城市\(i\),花费\(y_i\)建一个港口。还有\(m\)条没有修建的道路,第\(i\)条道路双向连接\(a_i\)和\(b_i\),修建这条道路需要......
  • CF1077E Thematic Contests 题解
    ThematicContests题意有\(n\)个问题,每个问题有一个分类\(a_i\)。现在要举办一些比赛,要求:一场比赛中所有题目的分类相同。所有比赛的分类是互不相同的。第一场比赛的题目数量任意,但从第二场开始,每一场比赛的题目数量都必须是前一场的两倍。求所有比赛的题目数量之和的......
  • abc235_d Multiply and Rotate 题解
    MultiplyandRotate题意给定两个整数\(a\)和\(n\),有一个整数\(x\),初始值为\(1\),有两种操作:将\(x\)变成\(x\timesa\)。在\(x>10\)且\(x\)不是十的倍数的情况下可以执行此操作:将\(x\)当成一个字符串,将其循环右移一次。求最少执行多少次操作能把\(x\)变......
  • P8786 李白打酒加强版 题解
    李白打酒加强版题目大意一开始酒显里有\(2\)斗酒,每遇到一次店就会把酒显里的酒数量(单位:斗)乘\(2\),每遇到一次花就把酒显里的酒喝掉\(1\)斗。这一路上一共遇到店\(n\)次,遇到花\(m\)次,且最后一次遇到的是花,酒显里没有一斗酒了。计算这一路上遇到店和花的顺序总共有......
  • YACS 2023年5月月赛 甲组 T1 子集和(六) 题解
    YACS老师说这是道分治,但就不告诉我怎么做,我硬是用线段树卡了过去...特别解气的是把AC图片发了过去(我就是在YACS上的编程课)莫队好了说点正经的,看到是谁谁谁的倍数就能想到DP,状态设计也很水啦!设f[i]为当前考虑到的区间内,子集和%k=i的数量。新加入一个元素时,循环0~......