首页 > 其他分享 >CodeForces 1895E Infinite Card Game

CodeForces 1895E Infinite Card Game

时间:2023-11-15 19:57:54浏览次数:38  
标签:typedef int CodeForces long mid Game maxn Infinite define

洛谷传送门

CF 传送门

容易转化成经典的有向图博弈模型。每张牌建一个点,若 \(x\) 能打败 \(y\) 就连一条 \(x \to y\) 的边。入度为 \(0\) 的点为必败态,之后类似拓扑排序倒推即可。

具体就是若存在边 \(u \to v\),若 \(u\) 为必败态则 \(v\) 为必胜态并加入队列;若 \(v\) 的所有前驱都是必胜态,那么 \(v\) 为必败态并加入队列。最后没考虑到的点即为平局态。有点类似 P9169 [省选联考 2023] 过河卒

朴素地做边数 \(O(n^2)\),无法承受。注意到若把双方的牌都按防御值排序,那么一个点的出边是一段连续的区间。设点 \(u\) 的连边区间为 \([l_u, r_u]\)。于是可以直接线段树维护每个点的入度,每当入度最小值为 \(0\) 就把对应的点 push 进队列并将这个点的状态设为必败态。线段树需要支持区间加,单点修改,查全局最小值及其位置。

还需要支持当 \(u\) 为必败态时把 \(u\) 的所有后继都设为必胜态。发现实际上进行操作的只有 \(u\) 的之前没设为必胜态的后继的点。套路地使用并查集,并查集上每个点的祖先为它下一个还没被设为必胜态的点,就可以暴力找到要更新的点了。

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

code
// Problem: E. Infinite Card Game
// Contest: Codeforces - Educational Codeforces Round 157 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1895/problem/E
// Memory Limit: 512 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 1000100;

int n, m, f[maxn], fa[maxn];
pii g[maxn];
struct node {
	int x, y;
} a[maxn], b[maxn];

int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}

namespace SGT {
	pii a[maxn << 2];
	int tag[maxn << 2];
	
	inline void pushup(int x) {
		a[x] = min(a[x << 1], a[x << 1 | 1]);
	}
	
	inline void pushdown(int x) {
		if (!tag[x]) {
			return;
		}
		a[x << 1].fst += tag[x];
		a[x << 1 | 1].fst += tag[x];
		tag[x << 1] += tag[x];
		tag[x << 1 | 1] += tag[x];
		tag[x] = 0;
	}
	
	void build(int rt, int l, int r) {
		tag[rt] = 0;
		if (l == r) {
			a[rt] = mkp(0, l);
			return;
		}
		int mid = (l + r) >> 1;
		build(rt << 1, l, mid);
		build(rt << 1 | 1, mid + 1, r);
		pushup(rt);
	}
	
	void update(int rt, int l, int r, int ql, int qr, int x) {
		if (ql > qr) {
			return;
		}
		if (ql <= l && r <= qr) {
			a[rt].fst += x;
			tag[rt] += x;
			return;
		}
		pushdown(rt);
		int mid = (l + r) >> 1;
		if (ql <= mid) {
			update(rt << 1, l, mid, ql, qr, x);
		}
		if (qr > mid) {
			update(rt << 1 | 1, mid + 1, r, ql, qr, x);
		}
		pushup(rt);
	}
	
	void modify(int rt, int l, int r, int x) {
		if (l == r) {
			a[rt].fst = 1e9;
			return;
		}
		pushdown(rt);
		int mid = (l + r) >> 1;
		(x <= mid) ? modify(rt << 1, l, mid, x) : modify(rt << 1 | 1, mid + 1, r, x);
		pushup(rt);
	}
}

void solve() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i].x);
	}
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i].y);
	}
	scanf("%d", &m);
	for (int i = 1; i <= m; ++i) {
		scanf("%d", &b[i].x);
	}
	for (int i = 1; i <= m; ++i) {
		scanf("%d", &b[i].y);
	}
	sort(a + 1, a + n + 1, [&](const node &a, const node &b) {
		return a.y < b.y;
	});
	sort(b + 1, b + m + 1, [&](const node &a, const node &b) {
		return a.y < b.y;
	});
	SGT::build(1, 1, n + m);
	for (int i = 1; i <= n; ++i) {
		int l = 1, r = m, pos = 0;
		while (l <= r) {
			int mid = (l + r) >> 1;
			if (a[i].x > b[mid].y) {
				pos = mid;
				l = mid + 1;
			} else {
				r = mid - 1;
			}
		}
		g[i] = mkp(n + 1, n + pos);
		SGT::update(1, 1, n + m, g[i].fst, g[i].scd, 1);
	}
	for (int i = 1; i <= m; ++i) {
		int l = 1, r = n, pos = 0;
		while (l <= r) {
			int mid = (l + r) >> 1;
			if (b[i].x > a[mid].y) {
				pos = mid;
				l = mid + 1;
			} else {
				r = mid - 1;
			}
		}
		g[i + n] = mkp(1, pos);
		SGT::update(1, 1, n + m, g[i + n].fst, g[i + n].scd, 1);
	}
	static int q[maxn];
	int hd = 1, tl = 0;
	for (int i = 1; i <= n + m; ++i) {
		f[i] = -1;
		fa[i] = i;
	}
	fa[n + m + 1] = n + m + 1;
	while (SGT::a[1].fst == 0) {
		int u = SGT::a[1].scd;
		f[u] = 0;
		q[++tl] = u;
		SGT::modify(1, 1, n + m, u);
	}
	while (hd <= tl) {
		int u = q[hd++];
		if (f[u] == 0) {
			for (int i = find(g[u].fst); i <= g[u].scd; i = find(i + 1)) {
				f[i] = 1;
				q[++tl] = i;
				fa[i] = i + 1;
			}
		}
		SGT::update(1, 1, n + m, g[u].fst, g[u].scd, -1);
		while (SGT::a[1].fst == 0) {
			int v = SGT::a[1].scd;
			SGT::modify(1, 1, n + m, v);
			if (f[v] == -1) {
				f[v] = 0;
				q[++tl] = v;
			}
		}
	}
	int c1 = 0, c2 = 0, c3 = 0;
	for (int i = 1; i <= n; ++i) {
		if (f[i] == 0) {
			++c1;
		} else if (f[i] == -1) {
			++c2;
		} else {
			++c3;
		}
	}
	printf("%d %d %d\n", c1, c2, c3);
}

int main() {
	int T = 1;
	scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:typedef,int,CodeForces,long,mid,Game,maxn,Infinite,define
From: https://www.cnblogs.com/zltzlt-blog/p/17834638.html

相关文章

  • Codeforces Round 907 (Div. 2)
    \(A.SortingwithTwos\)https://codeforces.com/contest/1891/submission/232689614\(B.DejaVu\)https://codeforces.com/contest/1891/submission/232690141\(C.SmiloandMonsters\)https://codeforces.com/contest/1891/submission/232691988\(D.Suspic......
  • Codeforces Round 809 (Div. 2) D1. Chopping Carrots (Easy Version) 题解
    题意CodeforcesRound809(Div.2)D1.ChoppingCarrots(EasyVersion)给两个整数\(n,k\),一个数组\(a\),要求构造一个同样长度的数组\(p\),使得\(\max\limits_{1\lei\len}\left(\left\lfloor\frac{a_i}{p_i}\right\rfloor\right)-\min\limits_{1\lei\l......
  • Codeforces Round 906 (Div. 2)
    A.简单题B.简单题C.比赛时没做出来,赶着回宿舍,过了几天来补发现很简单秒掉D.Doremy'sConnectingPlan给定n个结点的图,每个点有一个权值a[i],开始时图上没有边,如果与点i相邻的点(包括点i)的权值的和记为Sum_i.给定一个常数c,如果Sum_i+Sum_j>=ijc,则可以在i和j上......
  • CF467B Fedor and New Game
    前言传送门本题思维难度:橙。本题代码难度:橙或红。综合难度:橙。本人代码码量位居第二,但是呢,我的空格多,所以,还不来看一下?题意根据题目,若两人一人有$j$,一人没$j$,则异或后,第$j$位为$1$。那么,题目转化为:已知有$m+1$个数,求出满足$a_i$异或$a_{m+1}$结果的$1$的......
  • 开发知识点-Pygame
    PygamePygame最小开发框架与最小游戏游戏开发入门单元开篇Pygame简介安装游戏开发入门语言开发工具的选择Pygame最小开发框架与最小游戏游戏开发入门单元开篇Pygame简介安装游戏开发入门语言开发工具的选择......
  • 【pwn】[HGAME 2023 week1]choose_the_seat --数组越界,劫持got表
    查一下程序保护情况发现是partialrelro,说明got表是可以修改的,下一步看代码逻辑看到这一段puts(&seats[16*v0]);存在数组越界的漏洞,因为上面的代码没有对v0进行负数的限制,v0可以是负数,我们来看一下seat的数据可以发现seat上面的数据就是got表,seat到exit的距离只需要传入......
  • CodeForces 1452E Two Editorials
    洛谷传送门CF传送门考虑枚举其中一个区间取\([i,i+K-1]\),考虑对于每个\(j\)一次性处理出,区间取\([j-K+1,j]\)多产生的贡献(即以\(j\)为右端点)。对于一个\([l_k,r_k]\),设其与\([i,i+K-1]\)的交长度为\(t\)。如果\(t=\min(r_k-l_k+1,K)\)则......
  • 【pwn】[HGAME 2023 week1]simple_shellcode --orw利用攻击
    先查看程序的保护状态可以看到,保护全开,拖进ida看主函数的逻辑可以看到有个mmap函数:mmap()函数是Unix和类Unix操作系统中的一个系统调用,用于在进程的地址空间中映射文件或者其它对象。这样做的好处是可以让文件直接映射到内存中,从而避免了频繁的文件I/O操作,提高了文件的读......
  • Codeforces Round 908 (Div. 2) A-D
    SecretSport观察到数据不大,直接摁住x和y枚举即可,方案合法当且仅当刚好打若干局,且赢最后一局的人是赢家#include<bits/stdc++.h>usingnamespacestd;voidsolve(){intn;cin>>n;strings;cin>>s;//xintwina=0,winb=0;for(inti=1;i<=n......
  • Codeforces Round 887 (Div. 2)
    https://codeforces.com/contest/1853C题感觉很不好写的样子,首先通过打表发现最后答案每次都是+n,那么我们考虑前i个,假如当前的ans+i仍然小于a[i+1],则没有影响,我们依然可以直接往后跳,否则,我们越过了a[i+1],那么我们应当加上i+1,注意,这有可能会导致往后面继续跳,比如13567,我们跳......