vp Codeforces Round 986 (Div. 2)

A. Alice's Adventures in "Chess"

题意:你从(0, 0)出发,重复固定的移动路径,问能不能经过(a, b)。

直接跑一百次就行,因为ab都很小(其实只要跑20次)。

点击查看代码
void solve() {
    int n, a, b;
    std::cin >> n >> a >> b;
    int x = 0, y = 0;
    std::string s;
    std::cin >> s;
    std::vector<std::pair<int, int> > c;
    for (auto & ch : s) {
    	if (ch == 'N') {
    		++ y;
    	} else if (ch == 'E') {
    		++ x;
    	} else if (ch == 'S') {
    		-- y;
    	} else {
    		-- x;
    	}

    	c.push_back({x, y});
    }

    for (int i = 0; i <= 100; ++ i) {
    	for (auto & it : c) {
    		if (it.first == a && it.second == b) {
    			std::cout << "YES\n";
    			return;
    		}

    		it.first += x; it.second += y;
    	}
    }

    std::cout << "NO\n";
}

B. Alice's Adventures in Permuting

题意:给你一个数组的生成式:\(a_i\)=(i-1)*b+c,每次把最大的数变成mex,问需要多少次可以变成0到n-1的排列。

自己写几个样例发现,如果全部数都小于n,那么b=1,c=0的情况是0次操作,b=0的话,c>=n是n次操作,c>=n-2是n-1次操作,其他都是-1。
对于数组里有大于等于n的数的情况,那么每次会把一个大于等于n的数变成mex,如果有m个大于等于n的数,就会操作m次,我是用二分找的这个m。

点击查看代码
void solve() {
	using i128 = __int128;
    i64 n, b, c;
    std::cin >> n >> b >> c;
    if (b == 0 && c >= n) {
    	std::cout << n << "\n";
    	return;
    }

    if (b == 0 && c >= n - 2) {
    	std::cout << n - 1 << "\n";
    	return;
    }


    if ((i128)(n - 1) * (i128)b + c == n - 1) {
		std::cout << 0 << "\n";	
		return;
	}

    if ((i128)(n - 1) * (i128)b + c < n - 1) {
		std::cout << -1 << "\n";	
		return;
	}

    i64 l = 1, r = n;
    while (l < r) {
    	i64 mid = l + r >> 1;
    	if ((i128)(mid - 1) * (i128)b + c >= n) {
    		r = mid;
    	} else {
    		l = mid + 1;
    	}
    }

    std::cout << n - l + 1 << "\n";
}

C. Alice's Adventures in Cutting Cake

题意:你要把一个数组分成m+1组,每一组都由连续的元素组成,其中m组每组总和要大于等于v,要让剩下属于我们的一组在满足这些条件下的总和最大。

如果我们以i开始,那么前面i-1个应该正好分成满足条件的几组,不然我们的开始位置可以往前移。那么我们可以从前往后枚举前面分几组,那么我们这组能取到后面哪个位置呢?为了让后面的组对我们的影响最小,应该从最后开始分给他们,那就预处理从后面开始到第i个可以取满几个组,那么如果我们前面有了k组,那么可以二分后面分成m-k组的最大下标,那么中间的数就都属于我们。

点击查看代码
void solve() {
    int n, m;
    i64 v;
    std::cin >> n >> m >> v;
    std::vector<i64> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::vector<i64> sum(n + 1);
    for (int i = 0; i < n; ++ i) {
    	sum[i + 1] = sum[i] + a[i];
    }

    std::vector<int> cnt(n + 2);
   	for (int i = n; i >= 1; -- i) {
   		int j = i;
   		while (j && sum[i] - sum[j - 1] < v) {
   			-- j;
   		}

   		if (j) {
   			cnt[j] = cnt[i + 1] + 1;
   		}

   		i = j;
   	}

   	for (int i = n; i >= 1; -- i) {
   		cnt[i] = std::max(cnt[i], cnt[i + 1]);
   	}

   	if (cnt[1] < m) {
   		std::cout << -1 << "\n";
   		return;
   	};

   	i64 ans = 0;
   	for (int i = 1, k = m; i <= n; ++ i, -- k) {
   		int l = i + 1, r = n + 1;
   		while (l < r) {
   			int mid = l + r + 1 >> 1;
   			if (cnt[mid] >= k) {
   				l = mid;
   			} else {
   				r = mid - 1;
   			}
   		}

   		if (cnt[l] >= k) {
   			ans = std::max(ans, sum[l - 1] - sum[i - 1]);
   		}

   		int j = i;
   		while (j <= n && sum[j] - sum[i - 1] < v) {
   			++ j;
   		}

   		i = j;
   	}

   	std::cout << ans << "\n";
}

D. Alice's Adventures in Cards

题意:我们手里有一个1,希望它变到n,我们可以和三个人交换,每个人对i都有一个重视值,如果第i个数重视值大于第j个数,那么我们可以用j换i。我们只可以换比手里更大的数。要求给定一个操作序列。

正解好像是dp,我写了个差不多搜索的东西。
三个set存分别存{\(a_i\), i}, {\(b_i\), i}, {\(c_i\), i}。
开始我们把1入队,用优先队列每次出最小的数。然后把还没进队的比当前数小的数从三个set里都删了。然后三个set分别lower_bound一下枚举比当前数值小的数,求个最短路记录转移,最后把新入队的数统一从三个set里删了。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n + 1), b(n + 1), c(n + 1);
    for (int i = 1; i <= n; ++ i) {
    	std::cin >> a[i];
    }

    for (int i = 1; i <= n; ++ i) {
    	std::cin >> b[i];
    }

    for (int i = 1; i <= n; ++ i) {
    	std::cin >> c[i];
    }

    std::vector<int> pre(n + 1);
    std::vector<std::string> op(n + 1);
    std::priority_queue<int, std::vector<int>, std::greater<int> > q;
    q.push(1);
    std::set<int> s;
    std::set<std::pair<int, int> > sa, sb, sc;
    for (int i = 2; i <= n; ++ i) {
    	s.insert(i);
    	sa.insert({a[i], i});
    	sb.insert({b[i], i});
    	sc.insert({c[i], i});
    }

    while (q.size()) {
    	int u = q.top(); q.pop();
    	while (s.size() && *s.begin() < u) {
    		int id = *s.begin();
    		sa.erase({a[id], id});
    		sb.erase({b[id], id});
    		sc.erase({c[id], id});
    		s.erase(s.begin());
    	}

    	std::vector<int> del;

    	auto ia = sa.lower_bound({a[u], 0});
    	while (ia != sa.begin()) {
    		-- ia;
    		int v = ia->second;
			if (op[v].empty()) {
				pre[v] = u;
				op[v] = "Q";
				q.push(v);
				del.push_back(v);
	    	}
	    }

	    auto ib = sb.lower_bound({b[u], 0});
    	while (ib != sb.begin()) {
    		-- ib;
    		int v = ib->second;
			if (op[v].empty()) {
				pre[v] = u;
				op[v] = "K";
				q.push(v);
				del.push_back(v);
	    	}
	    }

	    auto ic = sc.lower_bound({c[u], 0});
    	while (ic != sc.begin()) {
    		-- ic;
    		int v = ic->second;
			if (op[v].empty()) {
				pre[v] = u;
				op[v] = "J";

				q.push(v);
				del.push_back(v);
	    	}
	    }

    	for (auto & id : del) {
    		sa.erase({a[id], id});
    		sb.erase({b[id], id});
    		sc.erase({c[id], id});
    		s.erase(id);
    	}
    }

    if (s.count(n)) {
    	std::cout << "NO\n";
    } else {
    	std::cout << "YES\n";
    	std::vector<std::string> ans;
    	int i = n;
    	while (i > 1) {
    		ans.push_back(op[i] + " " + std::to_string(i));
    		i = pre[i];
    	}

    	std::reverse(ans.begin(), ans.end());
    	std::cout << ans.size() << "\n";
    	for (auto & s : ans) {
    		std::cout << s << "\n";
    	}
    }
}

E. Alice's Adventures in the Rabbit Hole

题意:有一棵树,1号点是出口,叶节点是死路,每次有二分之一的概率由你决定移动到哪个相邻节点,否则由皇后决定,皇后希望你走到叶子上,你希望走到出口上。求每个点逃脱的概率。

我们肯定是往上爬,皇后会选择离叶子近的那一边。我们的决策显然是这样,而皇后因为不想让我们到出口,肯定不会帮我们往上爬,而她又要让我们去叶子,肯定是越近的越好。

设f[u]表示这个节点逃脱成功的概率。
根节点概率是1,叶子是0,其他概率是f[u]=\(\frac{1}{2}\)(f[fa]+fa[v]),离叶子节点距离1的话是f[u]=\(\frac{1}{2}\)f[fa],离叶子节点距离2就是f[u]=\(\frac{1}{2}\)(f[fa]+fa[v])=\(\frac{1}{2}\)(f[fa]+\(\frac{1}{2}\)f[u])->\(\frac{3}{4}\)f[u]=\(\frac{1}{2}\)f[fa],整理得f[u]=\(\frac{2}{3}\)f[fa]。我们可以继续算,也可以猜一下,发现f[u]=f[fa]\(\frac{d[u]}{(d[u]+1)}\)。d[u]是u到叶子的最短距离。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<std::vector<int> > adj(n);
    for (int i = 1; i < n; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
    }

    std::vector<int> d(n, 1e9);
    auto dfs = [&](auto self, int u, int fa) -> void {
    	for (auto & v : adj[u]) {
    		if (v == fa) {
    			continue;
    		}

    		self(self, v, u);

    		d[u] = std::min(d[u], d[v] + 1);
    	}

    	if (d[u] == 1e9) {
    		d[u] = 0;	
    	}
    };

    dfs(dfs, 0, -1);

    std::vector<Z> f(n);
    auto dfs1 = [&](auto self, int u, int fa) -> void {
    	for (auto & v : adj[u]) {
    		if (v == fa) {
    			continue;
    		}

    		f[v] = f[u] * (Z)d[v] / (Z)(d[v] + 1);
    		self(self, v, u);
    	}
    };

    f[0] = 1;
    dfs1(dfs1, 0, -1);
    for (int i = 0; i < n; ++ i) {
    	std::cout << f[i] << " \n"[i == n - 1];
    }
}
posted @   maburb  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示